Create a jobs board with codeigniter – part 1

GFX9.COM share Create a jobs board with codeigniter – part 1, you can download now.

Tags: , .


In this two-part series we'll be creating a basic 'Jobs Board', similar to FreelanceSwitch Jobs and Smashing Jobs, using the CodeIgniter PHP framework.


Also available in this series:

  1. Create a Jobs Board with CodeIgniter - Part 1
  2. Create a Jobs Board with CodeIgniter - Part 2

Previous Experience Required

This tutorial series will not serve as a beginners guide to CodeIgniter, however you don't need much experience with it either. I'd recommend to at the least, have an understanding of what an MVC (Model, View, Controller) system is and to have had some experience creating a simple application with CodeIgniter.


What We're Creating

By the end of the tutorial, we'll have created a fairly basic, 'job board' for businesses to list job openings they have available. This is very similar to the boards provided by FreelanceSwitch and Smashing Magazine; among others.


Above is our homepage. Not outstandingly pretty, but we won't be concentrating too much on the front-end design. Down the left side are the recent listings, and the sidebar contains the categories. Click a category link, to be taken to the listings for that category:


Click on a job listing to be taken to it's details page:


Finally, a user can add their own listings by clicking the 'Add a Listing' button on the sidebar of every page:


All the form fields include all the normal validation you'd expect from a secure PHP application in order to stop SQL injections, XSS attacks etc. – CodeIgniter makes this very simple.


Preparation

First, create a new database in whatever way you usually do (e.g. phpMyAdmin). Run the following SQL query block on the database to create our 'categories' and 'jobs' tables, including some dummy data:

 
DROP TABLE IF EXISTS `categories`; 
 
CREATE TABLE `categories` ( 
  `id` int(11) NOT NULL auto_increment, 
  `name` varchar(255) default NULL, 
  PRIMARY KEY  (`id`) 
); 
 
INSERT INTO `categories` (`id`,`name`) 
VALUES 
	(1,'Design'), 
	(2,'Development'), 
	(3,'Writing'), 
	(4,'Illustration'), 
	(5,'Flash'), 
	(6,'Misc'); 
 
DROP TABLE IF EXISTS `jobs`; 
 
CREATE TABLE `jobs` ( 
  `id` int(11) NOT NULL auto_increment, 
  `title` varchar(250) default NULL, 
  `body` text, 
  `location` varchar(100) default NULL, 
  `company` varchar(100) default NULL, 
  `posted` decimal(10,0) default NULL, 
  `type` varchar(30) default NULL, 
  `category` int(11) default NULL, 
  `email` varchar(250) default NULL, 
  `url` varchar(250) default NULL, 
  `ipaddress` varchar(15) default NULL, 
  PRIMARY KEY  (`id`) 
); 
 
INSERT INTO `jobs` (`id`,`title`,`body`,`location`,`company`,`posted`,`type`,`category`,`email`,`url`,`ipaddress`) 
VALUES 
	(1,'WordPress Developer Required','Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque id massa et nulla fringilla sodales sit amet a justo.nCras ac urna felis. Fusce mollis sem eget dolor fermentum ac tempus justo laoreet. Sed rutrum, purus vel dictum ultrices, diam quam tempor velit, non ultricies augue elit nec tellus.nnNunc fringilla, ipsum vel porta hendrerit, magna lorem consectetur urna, quis euismod orci arcu vel neque. Vivamus adipiscing felis sed turpis volutpat ac euismod dui rutrum. Vestibulum massa libero, lacinia a sollicitudin vitae.nnDonec in lectus dolor.','London, England','The British Foreign Office',1244572411,'Full Time',2,'aa@bb.com','http://mi5.gov.uk',NULL), 
	(2,'Another Position','Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque id massa et nulla fringilla sodales sit amet a justo.nCras ac urna felis. Fusce mollis sem eget dolor fermentum ac tempus justo laoreet. Sed rutrum, purus vel dictum ultrices, diam quam tempor velit, non ultricies augue elit nec tellus.nnNunc fringilla, ipsum vel porta hendrerit, magna lorem consectetur urna, quis euismod orci arcu vel neque. Vivamus adipiscing felis sed turpis volutpat ac euismod dui rutrum. Vestibulum massa libero, lacinia a sollicitudin vitae.nnDonec in lectus dolor.','Anywhere','Microsoft',1244665244,'Freelance',2,'a@a.com','http://www.microsoft.com',NULL), 
	(3,'This is a test','Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque id massa et nulla fringilla sodales sit amet a justo.nCras ac urna felis. Fusce mollis sem eget dolor fermentum ac tempus justo laoreet. Sed rutrum, purus vel dictum ultrices, diam quam tempor velit, non ultricies augue elit nec tellus.nnNunc fringilla, ipsum vel porta hendrerit, magna lorem consectetur urna, quis euismod orci arcu vel neque. Vivamus adipiscing felis sed turpis volutpat ac euismod dui rutrum. Vestibulum massa libero, lacinia a sollicitudin vitae.nnDonec in lectus dolor.','New York City, USA','United Nations',1244817051,'Full Time',1,'bob@theunitednations.co.cc','http://theun-home.webs.com','127.0.0.1'), 
	(15,'Blsh','Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque id massa et nulla fringilla sodales sit amet a justo.nCras ac urna felis. Fusce mollis sem eget dolor fermentum ac tempus justo laoreet. Sed rutrum, purus vel dictum ultrices, diam quam tempor velit, non ultricies augue elit nec tellus.nnNunc fringilla, ipsum vel porta hendrerit, magna lorem consectetur urna, quis euismod orci arcu vel neque. Vivamus adipiscing felis sed turpis volutpat ac euismod dui rutrum. Vestibulum massa libero, lacinia a sollicitudin vitae.nnDonec in lectus dolor.','Sydney, Australia','Company2',1244839187,'Full Time',1,'djid@dnid.com','','0.0.0.0'), 
	(17,'This is a test','Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque id massa et nulla fringilla sodales sit amet a justo.nCras ac urna felis. Fusce mollis sem eget dolor fermentum ac tempus justo laoreet. Sed rutrum, purus vel dictum ultrices, diam quam tempor velit, non ultricies augue elit nec tellus.nnNunc fringilla, ipsum vel porta hendrerit, magna lorem consectetur urna, quis euismod orci arcu vel neque. Vivamus adipiscing felis sed turpis volutpat ac euismod dui rutrum. Vestibulum massa libero, lacinia a sollicitudin vitae.nnDonec in lectus dolor.','New York City, USA','United Nations',1244994501,'Full Time',2,'bob@un-jobs.com','http://un.com','0.0.0.0');

Download the latest CodeIgniter release from their website (version 1.7.1 at time of writing), extract and send the folder to your web server (either your localhost or external server). I've renamed the folder jobs-tut, and you should have the following files inside:


The /system/application/ folder is where the majority of our coding will take place – so unless otherwise stated, assume all file and directory references start in this folder. First, CodeIgniter needs to be configured. Open /config/config.php.

Set the $config['base_url'] variable to the URL of the root of your CodeIgniter install. For me, this is http://localhost/jobs-tut/ – remember the trailing slash!

Scroll past the rest of the settings to $config['global_xss_filtering']. Set this to TRUE. CodeIgniter will automatically filter all POST, GET and COOKIE data for us, turning any code which may be harmful into HTML entities. This will save us having to filter the data manually.

Save and close the config file.

Inside /config/database.php, enter your database details into the areas provided.

Finally, we'll be making use of several of CodeIgniter's built-in libraries and helpers. We could load these manually in the controller, however we'll auto-load them instead.

Open /config/autoload.php. We will be using the 'database' and 'form_validation' libraries in this project, so set $autoload['libraries'] to:

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

As for helpers, we will be using 'url', 'form' and 'security'. Set $autoload['helper'] to:

 
$autoload['helper'] = array('url','form','security');

The Controller

In /config/routes.php, set the default controller to jobs:

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

CodeIgniter will now use the Jobs controller (which we haven't yet created) when someone visits the website. This will be our only controller, so let's create it now:

Inside the /controllers/ directory, create a file named jobs.php. Start this file with the following code:

 

If you're familiar with CodeIgniter, you'll know this is the starting code for any Controller. The first function, which must have the same name as the controller, is known as the 'constructor'.

All models and controllers in CodeIgniter must begin with a capital letter.

Inside the constructor function, include the next two lines:

 
$data['sitetitle'] = 'Nettuts Job Board'; 
$this->load->vars($data);

The first line we just set the site's title. This will be used later in the view to create our title tags.

The second line makes the $data array available inside our models and view. CodeIgniter will actually de-compile this array when passing it to a model or view, so that the title can be accessed simply as $sitetitle, and not $data['sitetitle'].

CodeIgniter & URLs

Before continuing, a quick note on how CodeIgniter handles URLs. By the end of the tutorial, one of our URLs will look something like this:

http://localhost/jobs-tut/index.php/jobs/details/3/

/jobs/ is the name of the controller class. /details/ is the function inside the controller being called, and /3/ is an extra segment which in this case, is the ID of the job we're looking at the details of.

Our listings page will be at

http://localhost/jobs-tut/index.php/jobs/listings/.

You may remove the 'index.php' in your URLs by following the directions in the 'CI User Guide'.


The Models

In the controller, we need to create the 'listings' function (as I mentioned previously in the URLs section). Add the following to your controller:

 
function index() { 
	redirect('jobs/listings'); 
} 
 
function listings() { 
	echo 'hello world'; 
}

The index() function is using a redirect to send all traffic to the root of the site to the listings() function. This is to future-proof the application if you ever decide to use a front-page other than the main listings page.

If you open the site in your browser now, the URL should be redirected to index.php/jobs/listings, and you should see our 'hello world' message.


Now we know everything is working correctly, replace the 'hello world' message with the following:

 
$data['listings'] = $this->MJobs->get_listings(); 
$this->load->view('listings', $data);

On the first line, we are calling the get_listings() function which will be inside the MJobs model, and the result will be placed inside $data['listings'].

The second line is loading the 'listings' view we'll create shortly. The second parameter is passing the $data array into the view so we can access the data there.

By the end of the tutorial, we'll only need two models: one for retrieving and adding job listings, the other for managing the categories. The listings model will be named 'MJobs', and categories 'MCats' – the 'M' simply makes it obvious it is a model. Including an 'M' prefix on models isn't necessary, it's just the method I learnt. Others use a '_model' suffix, or some don't use it at all.

Before creating the model, though, CodeIgniter needs to have actually loaded the model. We can do this in one of two ways: by adding the models we need into the /config/autoload.php file; or loading them in a function in the controller like so:

 
$this->load->model('MJobs');

As we'll be using each model on almost every page, we'll add them to the autoload. Inside /config/autoload.php, set $autoload['model'] to:

 
$autoload['model'] = array('MJobs', 'MCats');

Inside /models/ create two new files, with the following inside:

mjobs.php

 

And mcats.php

 

The code to create a model is very similar to creating the controller.

The 'Jobs' Model

Inside the MJobs model, we can now create the get_listings() function being called from the controller. Following the model constructor function, continue with the following code:

 
function get_listings() { 
	$data = array(); 
	 
	$this->db->order_by('id', 'desc'); 
	$q = $this->db->get('jobs'); 
}

Firstly inside the get_listings() function, we specify $data as an array. Below that is our SQL command to retrieve data from the database. Here, we are using CodeIgniter's Active Record class to interact with the database in an easier manner.

For reference, the code we are using is the equivalent to the following in normal PHP:

 
$q = mysql_query('SELECT * FROM jobs ORDER BY id DESC');

Alternatively, you can run native SQL commands through CodeIgniter if you'd prefer:

 
$q = $this->db->query('SELECT * FROM jobs ORDER BY id DESC');

However when using Active Record statements, CodeIgniter automatically filters and escapes all data before running the query, thus saving us from doing so ourselves if we were to use normal SQL commands instead.

For that reason, we're going to use Active Record statements throughout the application :).

Continuing on, include the following at the end of get_listings():

 
if ($q->num_rows() > 0) { 
	foreach ($q->result_array() as $row) { 
		$data[] = $row; 
	} 
} 
 
$q->free_result(); 
return $data;

This should be quite self-explanatory. First we check whether any results were returned from the database by running num_rows() on $q – in other words if the number of results are greater than 0.

We then 'foreach' through the results and place them into the $data array.

At the end we run free_result() on $q – this frees up any memory taken up by temporarily storing the results. While this isn't absolutely necessary on a small-scale application like this (as PHP should take care of this automatically), it's a good habit to get into for when you develop more intensive code.

Finally, we return the $data array to the controller.


Listings View

You will remember that in the controller's listings() function we loaded a view named 'listings', we'll create that now. Inside /views/ create four files: listings.php, header.php, sidebar.php and footer.php.

We haven't yet referenced the header, sidebar and footer files; we will soon.

Inside listings.php, start with the following:

 
load->view('header'); 
 
if ($listings) { 
 
	echo '

All Listings ' . count($listings) . '

'; echo '
    '; $counter = 0; foreach ($listings as $row) { $counter++; } echo '
'; } else { echo '

No Listings Available

'; echo '

There are currently no active listings.

'; } $this->load->view('sidebar'); $this->load->view('footer'); # End of file /views/listings.php

There's nothing too out of the ordinary here. Since we'll be using the same header, sidebar and footer across every page of the site, it makes sense to only code these once, and reference them when needed. We do this right at the top and at the bottom of this page. Loading a view file is exactly the same as how we did inside the controller.

Next, you'll remember that the controller automatically extracts the $data array before passing it to the view – for example anything in $data['listings'] is referenced in the view simply as $listings.

If there are listings available, we display the title, open an unordered list and start a foreach loop. The $counter will be used to create zebra-striping (alternate row highlighting) on the listings. Also note we use count($listings) to display the total number of listings available.

If there are no listings, we display a quick error message and finish off the page.

Now include the main listing display code directly below $counter++;:

 
?> 
 
> 
 
 
 
 
 
	 
	 
		|  
		|  
	 
	 
	 
		timeword->convert($posted, $current); ?> ago 
	 
 
 
 
 
 

Right at the top we use the counter to add a class of 'alt' to every-other row.

Further down we make use of CodeIgniter's URI helper in the form of the site_url() function. This accepts an array as a parameter which contains the URI segments to append to the address. For me, the function will output:

http://localhost/jobs-tut/index.php/jobs/details/1/

Where "1" is the ID of the listing as it is in the database.

We then continue to create a fairly normal layout using HTML – note that to include the data we retrieved from the database we use $row['title'] (where "title" is the name of the field in the database).

One part to highlight is where we display how long ago the listing was posted: $this->timeword->convert(). "timeword" is a custom CodeIgniter Library which we'll create next.


Creating a CI Library

Creating a custom CodeIgniter library class is essentially wrapping a normal PHP function in a class and CodeIgniter tags. The library we'll be creating is a wrapper for this function which calculates the difference between two dates, and outputs it in human-readable terms e.g. "less than 5 seconds", "about 1 month", "over 4 years" etc.

The function itself is a PHP port of Ruby on Rail's distance_of_time_in_words_to_now method.

Inside /system/application/libraries/, create a file named Timeword.php – the capital letter at the beginning is important!

Start the file with the following:

 

Inside the class we insert a slightly-modified version of the function we're using:

 
function convert($from_time, $to_time = 0, $include_seconds = true) { 
	// If no 'To' time provided, use current time. 
	if($to_time == 0) { $to_time = time(); } 
	 
	$distance_in_minutes = round(abs($to_time - $from_time) / 60); 
	$distance_in_seconds = round(abs($to_time - $from_time)); 
 
	if ($distance_in_minutes >= 0 and $distance_in_minutes <= 1) { 
		if (!$include_seconds) { 
			return ($distance_in_minutes == 0) ? 'less than a minute' : '1 minute'; 
		} else { 
			if ($distance_in_seconds >= 0 and $distance_in_seconds <= 4) { 
				return 'less than 5 seconds'; 
			} elseif ($distance_in_seconds >= 5 and $distance_in_seconds <= 9) { 
				return 'less than 10 seconds'; 
			} elseif ($distance_in_seconds >= 10 and $distance_in_seconds <= 19) { 
				return 'less than 20 seconds'; 
			} elseif ($distance_in_seconds >= 20 and $distance_in_seconds <= 39) { 
				return 'half a minute'; 
			} elseif ($distance_in_seconds >= 40 and $distance_in_seconds <= 59) { 
				return 'less than a minute'; 
			} else { 
				return '1 minute'; 
			} 
		} 
	} elseif ($distance_in_minutes >= 2 and $distance_in_minutes <= 44) { 
		return $distance_in_minutes . ' minutes'; 
	} elseif ($distance_in_minutes >= 45 and $distance_in_minutes <= 89) { 
		return 'about 1 hour'; 
	} elseif ($distance_in_minutes >= 90 and $distance_in_minutes <= 1439) { 
		return 'about ' . round(floatval($distance_in_minutes) / 60.0) . ' hours'; 
	} elseif ($distance_in_minutes >= 1440 and $distance_in_minutes <= 2879) { 
		return '1 day'; 
	} elseif ($distance_in_minutes >= 2880 and $distance_in_minutes <= 43199) { 
		return 'about ' . round(floatval($distance_in_minutes) / 1440) . ' days'; 
	} elseif ($distance_in_minutes >= 43200 and $distance_in_minutes <= 86399) { 
		return 'about 1 month'; 
	} elseif ($distance_in_minutes >= 86400 and $distance_in_minutes <= 525599) { 
		return round(floatval($distance_in_minutes) / 43200) . ' months'; 
	} elseif ($distance_in_minutes >= 525600 and $distance_in_minutes <= 1051199) { 
		return 'about 1 year'; 
	} else { 
		return 'over ' . round(floatval($distance_in_minutes) / 525600) . ' years'; 
	} 
}

Finally, add the library to /config/autoload.php by changing $autoload['libraries'] to:

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

Check out the site in your web-browser, and other than the header, sidebar and footer files, the info from the database should be displaying correctly from the database, and you should see the Timeword library working, also.

I won't go into any further detail on creating libraries for CI as that isn't the purpose of the tutorial. But hopefully that was enough to demonstrate how easy it is to add your own PHP functions in your CI application without bloating code in your model or controller.


Header, Sidebar and Footer

Next on the list is the other views which we called from the Listings view. /views/header.php:

 
 
 
 
 
<?php echo $sitetitle; ?> 
 
 
 
 

Nothing particularly special, a normal XHTML 1.0 Strict document. Note we are echoing $sitetitle for the title tags, which was included in the controller's constructor function.

When including a CSS stylesheet, we are using the base_url() function, which outputs the URL of the root of the site (e.g. http://localhost/jobs-tut/) – with a trailing slash.

When we create the stylesheet, the /css/ folder will be created at the very root of our CodeIgniter files, in the same directory as /system/ and /user_guide/.

Next is sidebar.php. We make use of site_url() to build the links to the 'Add a Listing' page at /jobs/add/ and a link back to the main listings page.

 

Later we'll include a list of all the listings categories here in the sidebar.

Finally, this is footer.php: