Building a URL Shortener from Scratch Using CodeIgniter

Overview

Todays post is a little longer and a lot more in-depth. Today we will look at setting up a URL shortening service with CodeIgniter similar to bit.ly etc. Yes there is 100s already out there, this is just to see how easy it is in CodeIgniter. You can create this service in minutes without breaking a sweat.

Note is only a very basic demonstration. If you were to launch this into production your could easily expand this by adding:

  • Statistics
  • Catching for duplicates
  • Better CSS
  • AJAX
  • Custom short values

As you will see I will use the tools listed in my essential toolkit. I always recommend Coda, Sequel Pro and MAMP to all developers. These two tools will have you developing at light speed.

In this post we will touch on:

  • CodeIgniter configuration
  • URL routing
  • .htaccess
  • Database storing and retrieving records
  • Model, view, and controller separation of code
  • Receiving data inputs
  • The form helper
  • Form validation

Task

The site is very simple. The site will only have 1 controller (shorten), 1 model (short_url_model) and 1 view (get_url) to execute 2 possible user actions:

  1. Shorten long URLs (shorten->index)
  2. Redirect shorten URLs to the long URL (shorten->get_shorty)
There is also an error_404 for when the shorten URL cannot be found.

CodeIgniter Preparation

First we will get the latest copy of CodeIgniter from http://codeigniter.com/download.php

Unzip the package, rename the folder to short_url (or whatever), move the folder to your Sites directory (whatever that may be on your system). We will use MAMP to setup a local URL (e.g. short.local).

 

CodeIgniter Configuration

Now that we have the environment setup we will update the configuration settings for CodeIgniter. This is a massive time saver of CI.

Get rid of index.php? from the URL

To remove that annoying ‘index.php’ from the URL we will create the .htaccess file in the root directory of the project with the below code:

RewriteEngine on
RewriteCond $1 !^(index.php|images|robots.txt)
RewriteRule ^(.*)$ /index.php/$1 [L]

This will allow CodeIgniter to display you URLs without the “index.php?”, once we update the config.php file.

Config Files

We will configure our setup by updating the files in the Application->config folder.

Autoload.php

The autoload.php file calls the scripts to load every time there is a request to the application. It is really good for things like session and database libraries which 99% of the time you will be using. However this means these are loaded every single call and use processing power and time. In this case we will be almost always using the database and form_validation libraries and the URL helper so we will auto load them.

//libraries
$autoload['libraries'] = array('database','form_validation');

//helpers
$autoload['helper'] = array('url');

Config.php

Because we used the .htaccess file to modify the URLs so we no longer require “index.php?”, we will set the index_page to nothing in config.php (application->config->config.php):

$config['index_page'] = '';

Database.php

We will setup our database settings to call a new database called short by editing the database.php (application->config->database.php)

$db['default']['username'] = 'root';
$db['default']['password'] = 'root';
$db['default']['database'] = 'short';

At this point we will use Sequel Pro to connect to the MAMP’s MySQL server, and create a new database called short and a table urls using the SQL:

CREATE TABLE urls (
  id int(11) NOT NULL AUTO_INCREMENT,
  long_url varchar(250) DEFAULT NULL,
  PRIMARY KEY (id)
) ENGINE=MyISAM AUTO_INCREMENT=1000 DEFAULT CHARSET=latin1;

Routes.php (this is where the magic happens)

Now to the most critical part of the application, the routes file. routes.php (application->config->routes.php) will use a little hack to circumvent the way URLs and controllers normally work.

$route['default_controller'] = "shorten";
$route['404_override'] = 'shorten/get_shorty';
$route['error_404'] = 'shorten/error_404';

Basically the above code:

  • Sends http://short.local to the shorten controller’s index which loads the page to capture URLs from the user
  • To direct users to the short URLs we use the 404_override which is called, if the requested URL cannot be found, we will then use the get_shorty in the shorten controller
  • If a shorten URL cannot be found we send the user to http://short.local/error_404, which loads a 404 error message

That is it for our configuration. Pretty simple stuff.

The Controller

To power the site we will need to create a new file for the shorten controller (application->controllers->shorten.php) . The controller acts as a director for the traffic. In this case the controller has 3 methods:

  • index – loads the main view and handles requests for long URLs to be turned into short URLS
  • get_shorty – this is called by the 404_override and fetches from the model the long URL for the short URL
  • error_404 – If the short URL can not be found the user is redirected to 404_error which the routes file routes to this method
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class Shorten extends CI_Controller { //Shorten extends CI -note the capital S

    public function index() //called by default
    {
        data=array(); //data to be sent to the view 🙂
        if($this->input->post('url'))//did the user post a URL to be shorten?
        {
            $this->load->model('short_url_model');//load the model which deals with data for short URLs
            $short_url=$this->short_url_model->store_long_url();//store the URL and get back the shorten URL
            if($short_url)//using PHP's awesome power
            {
                $data['short_url']=$short_url;
            }
            else //there was an error
            {
                $data['error']=validation_errors();
            }
        }

        $this->load->view('get_url', $data);//load the single view get_url and send any data to it
    }

    public function get_shorty() //this function is called by the routes file using the 404_override 🙂
    {
        $this->load->model('short_url_model'); //load the model for dealing with short URLs
        $shorty=$this->uri->segment(1);//get the segment the user requested e.g. Nw from http://short.local/Nw
         redirect($this->short_url_model->get_long_url($shorty));//direct the user to the long URL the short URL is connected to 🙂 MAGIC
    }

    public function error_404() //a little error for when users enter an invalid short URL
    {
        $data['error']='Whoops cannot find that URL!';
        $this->load->view('get_url', $data);//load our single view 🙂
    }

}

All in all pretty simple and very lean. I find a lot of the CodeIgniter tutorials put too much business logic in the controllers reducing the the code reuse and make it harder to consistently enforce business rules across the entire application. The main thing to remember is a controller meant to just instruct everyone on what to do, not to do any heavy lifting.

The Model

Our power house for the site is our model (application->models->short_url_model.php). The model has two functions:

  1. Store the long URL -this validates the input from the user, inserts the long URL into the database, masks the ID of the record and returns it to the controller.
  2. Retrive the long URL – decodes the ID and searches the database for the ID and if it cannot be found return the 404 link.
<?php

class Short_url_model extends CI_Model {

    function store_long_url()
    {
    	$this->form_validation->set_rules('url', 'URL', 'trim|prep_url|required|min_length[5]|max_length[250]|xss_clean');
    	if($this->form_validation->run())
    	{
    		$this->db->insert('urls', array('long_url'=>$this->input->post('url')));
    		return str_replace('=','-', base64_encode($this->db->insert_id()));
    	}
    }

    function get_long_url($shorty='')
    {
    	$query=$this->db->get_where('urls', array('id'=> base64_decode(str_replace('-','=', $shorty))));
    	if($query->num_rows()>0)
    	{
    		foreach ($query->result() as $row)
			{
			    return $row->long_url;
			}
    	}
    	return '/error_404';
    }

}

The View

To display the App to the world we have our view:

<!DOCTYPE html>
<!--[if lt IE 7 ]> <html lang="en" class="no-js ie6"> <![endif]-->
<!--[if IE 7 ]>    <html lang="en" class="no-js ie7"> <![endif]-->
<!--[if IE 8 ]>    <html lang="en" class="no-js ie8"> <![endif]-->
<!--[if IE 9 ]>    <html lang="en" class="no-js ie9"> <![endif]-->
<!--[if (gt IE 9)|!(IE)]><!--> <html lang="en" class="no-js"> <!--<![endif]-->
<head>
	<meta charset="utf-8">
	<!--[if IE]><![endif]-->
	<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
	<title></title>
	<meta name="description" content="">
	<meta name="keywords" content="" />
	<meta name="author" content="">
	<meta name="viewport" content="width=device-width; initial-scale=1.0">
	<!-- !CSS -->
	<link rel="stylesheet" href="/css/style.css?v=1">
</head>
<!-- !Body -->
<body>
	<div id="container">
		<h1>URL Shortener</h1>
		<section id="main">
		<?php

$this->load->helper('form');

echo form_open();
echo form_label('URL to Shorten', 'url');
echo form_input('url');
echo form_submit('shorty','Get Shorty');
echo form_close();

if(isset($short_url))
{
	echo '<a href="'.base_url().$short_url.'" target="_blank" class="shorty_url">'.base_url().$short_url.'</a>';
}

if(isset($error))
{
	echo '<div class="errors">'.$error.'</div>';
}
?>

		</section><!-- /main -->

	</div><!--!/#container -->
</body>
</html>

And that is it.

Advertisements

3 thoughts on “Building a URL Shortener from Scratch Using CodeIgniter

  1. I may be wrong ( not an expert) but encoding with the default php base64_encode function in the model will produce a very large sequence if the number is greater than 4 digits, and for the shortning sake that won’t be good. ex. echo str_replace(‘=’,’-‘, base64_encode(9999)); will produce OTk5OQ–, and the same code with 999999 will produce OTk5OTk5 and so on… We need to assume that the number of URL to be stored could be bigger that 9999, in fact it would go beyond 11 digits, which would produce OTk5OTk5OTk5OTk- as a “short” url. But again.. not an expert.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s