Create a jobs board with codeigniter – part 2

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

Tags: , .


In part two we're continuing development on our 'Jobs Board' by implementing categories in the listings and coding the 'Add a Listing' page – complete with form validation checks.


Also available in this series:

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

Categories Sidebar

Next on the agenda is to display the categories in the sidebar. When a user clicks on a category link, the listings for that category will be displayed.

A quick note on the database: In the 'jobs' table, there is a field named 'category'. In this field we store the ID of the category it is stored in.
The 'categories' table has just two fields: 'id' and 'name'. The ID in the 'jobs.category' field corresponds to a row in the 'categories' table.

As the sidebar will be displayed on all pages, the categories data needs to be accessible site-wide. For that reason, we'll call a get_categories() function from the MCats model into the controller's constructor function.

Add the following below $data['sitetitle'] = 'Nettuts Job Board';:

 
$data['categories'] = $this->MCats->get_categories();

Now, inside the MCats model (/models/mcats.php), we'll create the get_categories() function:

 
function get_categories() { 
    $data = array(); 
     
    $this->db->order_by('id', 'asc'); 
    $q = $this->db->get('categories'); 
     
    if ($q->num_rows() > 0) { 
        foreach ($q->result_array() as $row) { 
            $data[] = $row; 
        } 
    } 
     
    $q->free_result(); 
    return $data; 
}

This function is very similar to the get_listings() function in the MJobs model, calling the data from the 'categories' table, sorted by the 'id' field in ascending order.

The View

Inside /views/sidebar.php, enter the following in place of the 'Categories will go here' HTML comment.

 
 
        
  • »
  • A simple foreach looping through the retrieved categories. With regard to site_url($segments), you'll remember we used the same technique to create a link from individual job listings, to their respective details page.
    For me, this will output the link: http://localhost/jobs-tut/index.php/jobs/listings/1/ (where "1" is the category ID).

    Refresh the site, and your sidebar on every page should now look like this:


    Just one problem: when you click a category link, all listings still display - not just the ones for the chosen category. To add this functionality, we'll need to make some alterations to the controller and the MJobs model.


    Category Listings

    As the URL for listings in a certain category is:
    http://localhost/jobs-tut/index.php/jobs/listings/1/
    we need to pass the third URI segment from the listings() function in the controller to the get_listings() function in the model. So change $data['listings'] = $this->MJobs->get_listings() in the controller to:

     
    $data['listings'] = $this->MJobs->get_listings($this->uri->segment(3));

    So now the category ID is being passed to get_listings(), we need to alter the model to put it to use.
    Inside MJobs, change function get_listings() { to function get_listings($category) {.

    We can now do simple check on whether $category exists. If it does, we know we have to retrieve listings from only that category; otherwise, we retrieve all listings as normal.
    Change the following:

     
    $this->db->order_by('id', 'desc'); 
    $q = $this->db->get('jobs');

    To:

     
    if ($category) { 
    	$options = array('category' => $category); 
    	$this->db->order_by('id', 'desc'); 
    	$q = $this->db->get_where('jobs', $options); 
    } 
    else { 
    	$this->db->order_by('id', 'desc'); 
    	$q = $this->db->get('jobs'); 
    }

    Test it out. Clicking a category should now display only listings from that category; and clicking 'All' should display all listings, regardless of category.

    There is, however, still one problem: the title. When browsing listings from an individual category, the title is still 'All Listings'. We need to change this to read 'Development Listings' (where "Development" is the name of the category from the database).


    To correct this, we'll create a new function in the MCats model to retrieve the details of the current category. Start by adding the following to the listings() function in the controller, just below where we call get_listings():

     
    $data['category'] = $this->MCats->get_current_cat($this->uri->segment(3));

    And create the following function inside the MCats model:

     
    function get_current_cat($id) { 
    	$data = array(); 
    	 
    	$options = array('id' => $id); 
    	$q = $this->db->get_where('categories', $options, 1); 
    	 
    	if ($q->num_rows() > 0) { 
    		foreach ($q->result_array() as $row) { 
    			$data = $row; 
    		} 
    	} 
    	 
    	$q->free_result(); 
    	return $data; 
    }

    Very similar to the other functions we have. This one runs the equivalent of the following SQL statement:

     
    SELECT * FROM 'categories' WHERE 'id' = $id LIMIT 1;

    Finally, we can put this data to use inside the view. If $category exists, we display a title containing the name of the category, otherwise we display the normal 'All Listings' title.
    Replace the following code in the listings view:

     
    echo '

    All Listings ' . count($listings) . '

    ';

    With:

     
    if ($category) { 
    	echo '

    ' . $category['name'] . ' Listings ' . count($listings) . '

    '; } else { echo '

    All Listings ' . count($listings) . '

    '; }


    Add a Listing

    The application's almost done – just the 'Add a Listing' page left to code. Start by creating the controller function for the add page. All we need to do is load the view:

     
    function add() { 
        $this->load->view('add'); 
    }

    Create the view file as /views/add.php and start with the following code:

     
    load->view('header'); 
     
    echo '

    Add a Listing

    '; $category_options = array(); foreach ($categories as $row) { $category_options[$row['id']] = $row['name']; } $type_options = array( 'Full Time' => 'Full Time', 'Part Time' => 'Part Time', 'Freelance' => 'Freelance' ); echo validation_errors('

    ','

    '); echo form_open('jobs/submit'); ?> load->view('sidebar'); $this->load->view('footer'); # End of file /views/add.php

    In $category_options we create an array holding the details of the categories from the database, in the format of 'id' => 'name'.
    We also create a $type_options array holding different job types (Full Time, Part Time and Freelance). These arrays will be used shortly to create drop-down menus to select them from.

    validation_errors() is a function from CodeIgniter's Form Validation class. Shortly, we'll implement validation checks on submitted form data, if there are errors, CodeIgniter will display them here.

    form_open() is a function from CodeIgniter's Form Helper which we're using. It's a quick way to create an opening form tag linking to the correct place. This would output as:

     
    

    Now, between the opening and closing form tags, create the actual form elements:

     
    
    Job Details

    Title:

    Category:

    Type:

    Location:

    Your Details

    Your/Company Name:

    URL:

    Email:

    On each form element, you will notice we set the default value to: set_value('title') (where "title" is the 'name' of the form element). This allows the Form Validation class to re-populate the data if it finds any errors with any of the form elements.
    For example, if the user doesn't fill in a required field, the page will reload, display the necessary errors, and re-populate the form so the user doesn't have to enter it again.

    Also notice how for the 'Category' and 'Type' options, instead of creating the drop-down using normal HTML, we're using the form_dropdown() CodeIgniter function (also included in the Form helper).
    We pass through two parameters for the function – the first being the 'name' for the drop-down (i.e. the name of the database field); and the second being an array containing options for the drop-down menu.

    Validation & Inserting to the Database

    Our form directs to the /jobs/submit/ page, so create that in the controller:

     
    function submit() { 
        $this->MJobs->submit_listing(); 
    }

    Here, we only load the MJobs model, everything will be handled there. In the model, create the submit_listing() function:

     
    function submit_listing() { 
        $this->form_validation->set_rules('title', 'Title', 'required|max_length[250]|htmlspecialchars'); 
        $this->form_validation->set_rules('category', 'Category', 'required'); 
        $this->form_validation->set_rules('body', 'Job Description', 'required|htmlspecialchars'); 
        $this->form_validation->set_rules('type', 'Job Type', 'required|max_length[30]'); 
        $this->form_validation->set_rules('location', 'Location', 'required|max_length[100]'); 
        $this->form_validation->set_rules('company', 'Your/Company Name', 'required|max_length[100]'); 
        $this->form_validation->set_rules('url', 'URL', 'max_length[100]|prep_url'); 
        $this->form_validation->set_rules('email', 'Email', 'required|max_length[100]|valid_email'); 
    }

    This is where the Form Validation class really comes into play. With just one line of code we can run multiple checks on submitted form data. The set_rules() function accepts three parameters: The 'name' value for the form element you want to check; a human-friendly name for the element which will be used in any error messages; and the tests you want to be run on the data, separated by pipes ("|").

    Many of the rules are self-explanatory – 'required' means the form element must be filled in, an error is returned otherwise; max_length[] is obvious; htmlspecialchars filters out html data (although this is likely not required); valid_email checks the data resembles an email address; and prep_url will ensure submitted URLs contain an 'http://' prefix, and will automatically add one otherwise.

    For a full list of the rules you can run on data, check the Form Validation page in the User Guide. You can also run any PHP functions which require a single parameter, as rules – such as htmlspecialchars() and md5().

    Following the rules, type the following:

     
    if ($this->form_validation->run() == FALSE) { 
        $this->load->view('add'); 
    } 
    else { 
        $data = array( 
            'title'     =>  $this->input->post('title'), 
            'category'  =>  $this->input->post('category'), 
            'body'      =>  $this->input->post('body'), 
            'type'      =>  $this->input->post('type'), 
            'location'  =>  $this->input->post('location'), 
            'company'   =>  $this->input->post('company'), 
            'url'       =>  $this->input->post('url'), 
            'email'     =>  $this->input->post('email'), 
            'ipaddress' =>  $this->input->ip_address(), 
            'posted'    =>  time() 
        ); 
        $this->db->insert('jobs', $data); 
        redirect('jobs/listings'); 
    }

    $this->form_validation->run() will execute the validation rules. If the validation does not pass, the function returns 'FALSE'. In this case, the /views/add.php view file is re-loaded, the appropriate validation errors will display and the form fields will automatically re-populate.


    If the validation passes (returns 'TRUE'), the POST data is assembled into an array with the user's IP address and the UNIX timestamp of the current time both appended to the form data.
    The $data is then inserted into the database using the $this->db->insert() Active Record function. Following that, the user is redirected to the main listings page where their new listing should be sitting at the top of the list.

    And… we're done!


    Summary

    It's Not Perfect…

    So we've just finished a simple jobs board using CodeIgniter in a very short amount of time. However this application, while it works, is far from perfect and there's plenty of improvements which could be made:

    1. Listings can not be assigned to multiple categories: Only being able to assign listings to one category may be fine for now, but it limits the expandability of the application. Thankfully this is a fairly minor problem, which could be addressed without too much hassle. But it would definitely have been better had this possibility been incorporated at the start.
    2. No protection from bots: There is currently no protection from possible database flooding by bots as we have no way of distinguishing bots from humans. However, CodeIgniter provides a Captcha helper built-in which could be incorporated into the application with ease.
    3. No protection from other forms of database flooding: Similarly to the previous point, there is no way to stop a single user from submitting multiple listings in quick succession. This could be solved by making use of the IP addresses we already log, and limiting listing submissions to one every x minutes.
    4. Caching: CodeIgniter includes an insanely easy method to cache a page, with just one line of code added to a function in the controller:
      $this->output->cache(n);
      (Where "n" is the number of minutes to cache the page for). At the very least, caching could be added to the /jobs/details/ pages.
    5. Administration: Probably most importantly, there is no administration user-interface manage listings and categories. Although with CodeIgniter's huge amount of helpers and libraries creating a basic, secure admin system wouldn't involve too much effort.

    Are you tempted?

    Hopefully this tutorial has demonstrated to all you CodeIgniter new-comers what a fantastic framework it is! We can build complex web applications in a short amount of time and with minimal effort – and using an MVC structure ensures your code remains logically separated and is easy to maintain.





    Similar content