Create a Codeigniter App From Scratch

Written by Brad Traversy on 04 October 2013.


In this tutorial we will be programming a Codeigniter based PHP application/website. It will be a simple todo application. You are free to do what you want with this app. We will be using "Netbeans" for an IDE. You are free to use Netbeans or any IDE or editor for this project.

Installing Codeigniter

The first thing we need to do is go to http://ellislab.com/codeigniter and click the green download button to download the package to your computer. In this tutorial we are using Xampp, which is a program that allows us to run an Apache server with PHP and MySQL. You can use Xampp, Wamp, Mamp or any other setup or hosting that uses Apache with PHP and MySQL. In Xampp, the "web" folder is at C:\xampp\htdocs. So we will create a new file called "mytodo" in the "C:\xampp\htdocs" folder.

After you download the zip file from the CI website, remove the following folders and files and place them in the mytodo folder...

  • application
  • system
  • index.php

ci1

The system folder in most cases, should remain untouched. This is where all the core libraries and functionality of CI are placed. The application folder is where we want to add our programming. Here is a quick outline of the folders in the application folder

  • cache - Contains all of the cached pages for your app
  • config - Where you set configuration values for CI. We will be getting familiar with this folder
  • controllers - We will place our custom controller classes here\
  • core - Base classes for your app
  • errors- App specific error logs
  • helpers - Include files
  • hooks - Support files
  • language - In this folder
  • libraries - Your own custom libraries
  • logs - error logs
  • models - Your models go here. This is where you can interact with your database and run queries
  • thirdparty - This is where 3rd party plugins go
  • views - This is where most work will go. This is the presentational part of your app

Next, change the "application" folder to "app" and the "system" to "sys" (or whatever name youd like). This is a security percaution. Hackers know what folders to look for in a CI app. This may or may not throw them off. In order for your installation to work, you need to go into the index.php file and change the values of $system_folder and $application_folder to the new folder names.

ci2

Optional: If you are using Netbeans, I suggest opening a new project so you can have full access to the file structure. Just go to "File->New Project", choose "PHP" and point to the "mytodo" folder. Now all your files should be displayed on the left.

After you change the folder names, visit "localhost/mytodo". You should see a welcome page like below. This page tells us where we can find this content. The controller is at app/controllers/welcome.php and the view is at app/views/welcome_message.php.

ci3

MVC Structure

If you are looking to build with CI, then I am assuming that you have at least a basic understanding of the MVC (Model View Controller) architecture. Controllers are the traffic directors. They are on the front lines with the user. Anytime a user is giving input in either a form, visiting a page, etc, they are interacting with the controller. Models are for data. If the user is looking for something in a database, the controller will fetch it from the model which interacts with the database. The view is the presentational part of the app. The view is what the user sees. The controller gets from the model and passes to the view.

Configuration

CI needs a little bit of configuration after installation. Navigate to the "config" folder in the app folder.

autoload.php

Lets first open up autoload.php. This is where we can load our packages, libraries, models and helpers. We can also load them in our pages but they will only work on those pages. Here we can make it so they are loaded on all pages. First, lets autoload the libraries that we want to use. On most sites, I enable a few certain ones even if I dont plan on using them.

Look for the following...

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

Change to the following...

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

This will allow us to use the database, add form validation and use sessions for user authentication. Now lets load out helpers. Helpers are small bits of code that allow use to use certain functions on the fly.

$autoload['helper'] = array();

Change to the following...

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

This allows us to optimize links, html forms and basic html.

That is all we need to load for now. We will need to add out models to the model array but we can do that later.

config.php

Here we need to specify a base url. In my case, it is localhost/mytodo. If that is yours, put the following...

$config['base_url'] ="http://localhost/mytodo"];

If you scroll down a little you'll see a value for the "index_page". We are going to enable friendly urls so remove the "index_page" and leave it blank like this...

$config['index_page'] = '';

If we do not do this, your urls will look like this - http://localhost/mytodo/index.php/about

The last thing we need to do is set an encryption key. Look for the following...

$config['encryption_key'} = '';

Now add any "key" you want. It can be any word or phrase

$config['encryption_key'} = 'mykey';

.htaccess File

The next thing we need to do to enable friendly urls is add and edit the .htaccess file. You must create a file called .htaccess in the ROOT of your installation. So it should be along side of the app and sys folder. In this file, type the following...

Options +FollowSymLinks 
Options -Indexes 
DirectoryIndex index.php 
RewriteEngine on 
RewriteCond $1 !^(index\.php|assets|images|css|js|install|robots\.txt|favicon\.ico) 
RewriteCond %{REQUEST_FILENAME} !-f 
RewriteCond %{REQUEST_FILENAME} !-d 
RewriteRule ^(.*)$ index.php?/$1 [L,QSA]

Basically, what this does is routes everything through the index.php file. The 5th line tells the server what folders can be accessed outside of the index.php folder. These are usually image folders, css, javascript, etc. We will create an "assets" folder for that stuff as well.

Now navigate to localhost/mytodo/welcome. You'll see it takes you to the same page as localhost/mytodo. The reason for that is we have the main controller set to "Welcome". In Codeigniter, we access cotrollers through urls. For example, lts use this link - http://localhost/mytodo/tasks. This url is going to lead us to the "tasks" controllers "index" function. If we go to http://localhost/mytodo/tasks/new, this wil bring us to the "tasks" controller "new" function. If we use http://localhost/mytodo/tasks/edit/2, this brings use to the "tasks" controllers "edit" function and passes in "1" as a parameter.

URI Segments

We can use the url helpers "segment" property to identify certain parts of the url.

$this->uri->segment(n)

As an example, take the following url

http://somesite.com/news/local/metro/newsstory

We can access the parts of this url like this...

$this->uri->segment(1) - News
$this->uri->segment(2) = local
$this->uri->segment(3) = metro
$this->uri->segment(4) = newsstory

Creating a controller and view

Ok, lets get back to our project. We can leave the welcome controller if we want but just to show you how, lets create a home controller and make that the default. First thing to do is create a new file in the app/controllers directory called home.php.

We can access the home controller at http://localhost/mytodo/home, BUT we will get an error and that is because there is no view. So in your app/views folder, create a file called home.php. In that file type the following...

<h1>Welcome to myTodo!</h1>
<p>myTodo us a simple and helpful application to help you manage your day to day tasks. You can add as k lists as you want and as ks as you want. myTodo is absolutely free! Have fun.</p>

There should be no PHP tags in this file yet. If we visit http://localhost/mytodo/home, we will still get an error and that is because we have not added a view to the index method (method and function mean the same thing). To do that go back into the home controller and add this...

<php
class Home extends CI_Controller{
	public function index(){
    	$this->load->view('home');
    }
}

Now visit http://localhost/mytodo/home. You should see the text in our view file. If we go back to http://localhost/mytodo, we still see that default welcome page. That is because it is our default controller. Lets change that and make our home controller our default. Do that by going to config/routes.php and look for the default controller value. Change it to the following...

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

Now your homepage should be your home controller. Now you can go ahead and delete the controllers/welcome.php file and the views/welcome_message.php file.

Passing Data from Controller to View

You can easily pass data to the view from the controller in the form of an array. You do this with the second parameter of the view load line. Lets make the welcome heading dynamic. We will set it in the controller and pass it to the view

<php
class Home extends CI_Controller{
	public function index(){
    	//Create the data
        $data = array('welcome_heading' => 'Welcome to myTodo!');
        //Load the view and pass data
    	$this->load->view('home',$data);
    }
}

Now we can use the variable $welcome_heading in the view file...

<h1><php echo $welcome_heading; ?></h1>
<p>myTodo us a simple and helpful application to help you manage your day to day tasks. You can add as k lists as you want and as ks as you want. myTodo is absolutely free! Have fun.</p>

Using Layouts

If you go to your homepage and view the source code, you will see there is no html structure. No head, body or even html tags. We could add this to our home.php file, but what about other pages? we do not want to have to add all of that stuff to every page. That defeats the purpose of a dynamic website. There are a hundred ways you can implement layouts/templates/etc, but this is one of the easiest and most effective.

Lets go to the views folder and create a folder called "layouts". In that folder, create a file called "main.php". This will hold all of our template content. I am going to use a simple bootstrap framework. Paste the following HTML into the main.php file.

<!DOCTYPE html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<a href="/index.php"><title>myTodo Task Manager</title></a>
<link href="/css/bootstrap.css" rel="stylesheet">
<link href="/css/custom.css" rel="stylesheet">
</head>
<body>
 <div class="navbar navbar-inverse navbar-fixed-top">
      <div class="navbar-inner">
        <div class="container-fluid">
          <a class="brand" href="#">myTodo</a>
          <div class="nav-collapse collapse">
            <p class="navbar-text pull-right">
             <!--RIGHT TOP CONTENT-->
			 
            </p>
            <ul class="nav">
              <li><a href="/index.php">Home</a></li>         
            </ul>
          </div><!--/.nav-collapse -->
        </div>
      </div>
    </div>
    <div class="container-fluid">
      <div class="row-fluid">
        <div class="span3">
          <div class="well sidebar-nav">
          <div style="margin:0 0 10px 10px;">
			<!--SIDEBAR CONTENT-->
			
          </div>
          </div><!--/.well -->
        </div><!--/span-->
        <div class="span9">
   		<!--MAIN CONTENT-->
			<?php $this->load->view($main_content); ?>
        </div><!--/span-->
		</div><!--/row-->
      <hr>
      <footer>
        <p>© Copyright 2013</p>
      </footer>
    </div><!--/.fluid-container-->
</body>
</html>

Notice the line

<?php $this->load->view($main_content); ?>

This is a placeholder for our views. So from now on, in our conrtoller methods, we will be including the layout and view like this...

$data['main_content'] = 'home';
$this->load->view('layouts/main', $data);

So now your home controller should look like this...

<?php
class Home extends CI_Controller {
    public function index(){
        $data = array('welcome_heading' => 'Welcome to myTodo!');
        
        //Load View & Layout
        $data['main_content'] = 'home';
        $this->load->view('layouts/main',$data);
    }
  
}

Your Bootstrap HTML is there now we need to add the css file. In your views/layouts/main.php file, replace your css include links with the following...

<link rel="stylesheet" type="text/css" href="/<?php echo base_url(); ?>assets/css/bootstrap.css" />
<link rel="stylesheet" type="text/css" href="/<?php echo base_url(); ?>assets/css/custom.css" />

The base_url() function is used to reference the core website url. In our case it is http://localhost/mytodo/. The ending slash is included in the function so no need to add it there.

Now you need to create a new folder in the ROOT called "assets". This should be alongside of your app and sys folders. In that folder, create a folder called css, a folder called images and a folder called js. <a href="http://techguystaging.com/demofiles/codeigniter_from_scratch/bootstrap.css">Download the bootstrap.css file from here</a> and create another blank css file called custom.css. This is where we will add new styles as the bootstrap.css shouldn't be touched.

Now visit your frontend. It should look like this...

ci4

Create & Connect a Database

Now that we have our site/app configured, we need to give it database access. The first thing we need to do is create a database. I am using Xampp and it comes with a tool called phpmyadmin. It comes with most web hosting accounts. If you don't have it on your server, you probably have something similar. To log in I am going to go to http://localhost/phpmyadmin.

ci5

Once logged in, click on the "Database" tab on the top menu. Give the new database a name. I will call mine mytodo. Once created, you will see it in the left hand sidebar. Click on it.

ci6

Now we need to create the tables. You can follow along below but if you have no phpmyadmin experience, I would suiggest downloading the .sql file from here and importing it through phpmyadmin using the "import" link on the top menu.

For the first table, lets call it "lists". It will have 5 fields

  • id - int -11 ai - Primary Key
  • list_name - varchar - 255
  • list_body - text
  • list_user_id - int - 11
  • create_date - timestamp - CURRENT_TIMESTAMP

Next table will be "tasks". It will have 8 fields

  • id - int -11 ai - Primary Key
  • task_name - varchar - 255
  • task_body - text
  • list_id - int -11
  • due_date - date
  • create_date - timestamp - CURRENT_TIMESTAMP
  • is_complete - tinyint - 1 - 0

Last table will be "users". It will have 7 fields

  • id - int -11 ai - Primary Key
  • first_name - varchar - 255
  • last_name - varchar - 255
  • email - varchar - 255
  • username - varchar - 255
  • password - varchar - 255
  • register_date - timestamp - CURRENT_TIMESTAMP

Now that our database is ready, we need to connect CI to it. Do this in the config/database.php file. Add your database credentials. Here is an example...

$active_group = 'default';
$active_record = TRUE;
$db['default']['hostname'] = 'localhost';
$db['default']['username'] = 'root';
$db['default']['password'] = 'yourpassword';
$db['default']['database'] = 'mytodo';
$db['default']['dbdriver'] = 'mysql';
$db['default']['dbprefix'] = '';
$db['default']['pconnect'] = TRUE;
$db['default']['db_debug'] = TRUE;
$db['default']['cache_on'] = FALSE;
$db['default']['cachedir'] = '';
$db['default']['char_set'] = 'utf8';
$db['default']['dbcollat'] = 'utf8_general_ci';
$db['default']['swap_pre'] = '';
$db['default']['autoinit'] = TRUE;
$db['default']['stricton'] = FALSE;

Thats it, now we are connected to the database.

Lists

The next thing that I want to do is create a "lists" controller. So create a file called lists.php in the controllers folder. Add the following...

<?php
class Lists extends CI_Controller {
    public function index(){
        $data['main_content'] = 'lists/index';
        $this->load->view('layouts/main',$data);
    }
  
}

Now we need to create a view. This is different than the home view because we will have multiple list views. So we want to create a folder named lists in the views folder. In that views/lists folder add the following 3 files...

  • index.php
  • add_list.php
  • edit_list.php

You can see in the lists controller above, the main content is pointing to the index.php file in the lists folder.

Open the views/lists/index.php file in your editor and add

<h1>Task Lists</h1>
<p>These are your current task lists</p>
<p>To create a new list - <a href="#">Click here</a>

Now you can go to http://localhost/lists and you should see your text

Adding Rows to the Lists Table via phpmyadmin

I want to be able to get the list fields from the database and print them on our lists page but we do not have anything in there yet. So go back to phpmyadmin and click on the "mytodo" table, then click on "lists", then click the "Insert" link from the top menu.

ci7

Enter a couple rows. I will enter 3 rows. Do NOT include the id field values. These are auto incremented

  • list_name: Family & Kids
  • list_body: This list is for family related stuff such as picking or dropping off the kids off for activities and sports
  • list_user_id: 1
  • list_name: Work Related
  • list_body: This list will track appointments, work schedules and meetings
  • list_user_id: 1
  • list_name: Misc List
  • list_body: This list will have tasks that are not family or work related
  • list_user_id: 1

So your lists table should look something like this...

ci8

Selecting & Displaying Lists

We have not created any models up to this point. So lets do that. Create a file called "list_model.php" in the models folder. Typically, controllers are plural and models are singular. In this file, lets use active record in a function to easily get all entries in the lists table.

<?php
class List_model extends CI_Model{
    public function get_all_lists(){
        $query = $this->db->get('lists');
        return $query->result();
    }
    
}

The name of the model should always start withn an uppercase letter and end with "_model".

The body of the "get_all_lists()" method is using active record to select the lists. It is returning an array ($query->result) with the results to the controller.

Now we can get the lists from the model using the controller, BUT first we need to load the model. We could autoload it in the autoload.php file we saw earlier, but I want to load it in a constructor only because we haven't talked about them yet. A constructor is just a method that automatically runs when you instanciate a class. This is a great place to load models and other stuff.

Change your current list controller to this...

<?php
class Lists extends CI_Controller {
    
    public function __construct() {
        parent::__construct();
        //Load model
        $this->load->model('List_model');
    }
    
    public function index(){
        //Get all lists from the model
        $data['lists'] = $this->List_model->get_all_lists();
        
        //Load view and layout
        $data['main_content'] = 'lists/index';
        $this->load->view('layouts/main',$data);
    }
  
}

Notice the __construct method. It must have the leading 2 underscores. This gets run automatically. So it's a perfect place to load stuff. Now that the model is loaded we can call functions from it and add the return value (in this case an array object) into the $data array.

$data['lists]' = $this->List_model->get_all_lists();

The View

Now, open up your views/lists/index.php file. We should be able to iterate through the lists that the controller got from the model and passed to us. Add this to the views/lists/index.php view file

<h1>Task Lists
<p>These are your current task lists</p>
<?php foreach ($lists as $list) : ?>
    <?php echo $list->list_name; ?><br />
<?php endforeach; ?>
    <br />
<p>To create a new list - <a href="#">Click here</a>

We can use the $lists array to iterate through using the foreach loop. We can then print out any property of that object with $list->list_name, etc

Your frontend list page should now look like this...

ci9

We want this to look a little better. Lets add a bit of style and also display the description of each list...

Make your views/lists/index.php file look like this...

<h1>Task Lists</h1>
<p>These are your current task lists</p>
<ul class="list_items">
<?php foreach ($lists as $list) : ?>
    <li>
        <div class="list_name"><a href="/<?php echo base_url(); ?>list/view/<?php echo $list->id; ?>"><?php echo $list->list_name; ?></a></div>
        <div class="list_body"><?php echo $list->list_body; ?></div>
    </li>
<?php endforeach; ?>
</ul>
    <br />
<p>To create a new list - <a href="#">Click here</a>

Now in your assets/custom.css file add this

ul.list_items li{
    padding:5px;
    border-bottom:1px #ccc dashed;
    list-style:square;
}

Now your lists should look like this...

ci10

Add a "Show" View for Single Lists

You'll notice in the views/lists/index.php view file, there is a link around the list title, This points to the show() method of the lists controller, along with the list id. So this will lead to the page for that particular single list. Lets create the show() method now. Go to your lists controller and add

public function show($id){
        //Get single list from the model
        $data['list'] = $this->List_model->get_list($id);
        
        //Load view and layout
        $data['main_content'] = 'lists/show';
        $this->load->view('layouts/main',$data);
    }

We need to grab the list info from the model. Add this to your models/list_model.php file..

 public function get_list($id){
        $this->db->select('*');
        $this->db->from('lists');
        $this->db->where('id',$id);
        $query = $this->db->get();
         if($query->num_rows() != 1){
            return FALSE;
        }
        return $query->row();
    }

This will get us one row because we are returning "$query->row()" with the specified id, which comes from the url

We also need a new view for the single display. We'll call it "show". So under views/lists, create a show.php file and add this...

<h1><php echo $list->list_name; ?></h1>
Created on <?php echo $list->create_date; ?>
&ltbr /><br />
<div style="max-width:500px;"><?php echo $list->list_body; ?></div>
<br /><br />
<h4>Current Open Tasks</h4>
<!--Tasks will go here-->
<br /><br />
<h4>Recently Completed</h4>
<!--Completed Tasks will go here-->

ci11

Lets add a menu item for the lists. Go to your views/layouts/main.php file and change the navigation list to

<ul class="nav">
	<li><a href="/<?php echo base_url(); ?>">Home</a> </li> 
	<li><a href="/<?php echo base_url(); ?>lists">Lists</a></li> 
</ul> 

Now you can easily access your lists from the frontend

Add Rows to the Tasks Table via phpmyadmin

The next thing we want to do is add some tasks in our lists. Let's go back to phpmyadmin and create a few tasks. Click on "tasks" then "insert". Now remember the "list_id" is a foreigh key field to the id of the list. So if you want to add a task for Work Related, then put a "2", because that is list 2. Here are my tasks

  • task_name: Bring kids to soccer
  • task_body: Need to bring both kids to soccer at 4:30
  • list_id:1
  • due_date:2013-09-19
  • is_complete:0
  • task_name: Pickup From Dance
  • task_body: Pickup daughter from dance at 6:30
  • list_id:1
  • due_date:2013-09-19
  • is_complete:0
  • task_name: Meeting with Boss
  • task_body: Have a lunch meering with boss at 11:30
  • list_id:2
  • due_date:2013-09-22
  • is_complete:0

Now that we have our tasks, we want to display the tasks on our list pages. Open up controllers/lists.php and add the following line to the show() method...

//Get all tasks for this list
$data['tasks'] = $this->List_model->get_list_tasks($id);

Now lets open up the list_model and create the get_list_tasks() method we are calling above.

public function get_list_tasks($list_id){
         $this->db->select('
tasks.task_name,
tasks.task_body,
tasks.id as task_id,
lists.list_name,
lists.list_body
'); $this->db->from('tasks'); $this->db->join('lists', 'lists.id = tasks.list_id'); $this->db->where('tasks.list_id',$list_id); $this->db->where('tasks.is_complete',0); $query = $this->db->get(); if($query->num_rows() < 1){ return FALSE; } return $query->result(); }

Here we are selecting certain fields from the lists and tasks table. You'll notice we are not selecting all with *, and we use the line tasks.id as task_id. We did this because both the lists and tasks table have a field named id. We need to be able to access the tasks id field so we selected it as task_id. We are using join to join in the lists table where lists.id = tasks.list_id. Then we are declaring a where clause because we only want tasks from the current list and then another where clause saying we only want tasks where "is_complete" is equal to 0. The list id is being passed in as a parameter. Finally, we are returning the result if there are any. If not, we are returning false.

Now since we added the line above to our index method in our lists class, we can use the variable $tasks in our view. Open up views/lists/show.php. Right under "Current Open Tasks", add the following...

<ul>
<?php foreach($tasks as $task) : ?>
    <li><?php echo $task->task_name; ?></li>
<?php endforeach; ?>
</ul>

Now we need our tasks that have been completed to display under "Recently Completed". We could go ahead and create a whole new model method to get completed tasks, but why not just add a parameter saying we want either true for active or false for inactive/completed? We need to slightly change the controller, model and view. First, the show() method in the controller

public function show($id){
        //Get all lists from the model
        $data['list'] = $this->List_model->get_list($id);
        //Get all completed tasks for this list
        $data['completed_tasks'] = $this->List_model->get_list_tasks($id,true);
        //Get all uncompleted tasks for this list
        $data['uncompleted_tasks'] = $this->List_model->get_list_tasks($id,false);
        
        //Load view and layout
        $data['main_content'] = 'lists/show';
        $this->load->view('layouts/main',$data);
}

You can see that we changed $data['tasks'] to $data['completed_tasks'] and added a value for uncompleted_tasks. We also added a true and false parameter. We will see how this works in the model method get_list_tasks().

public function get_list_tasks($list_id,$active = true){
        $this->db->select('
tasks.task_name,
tasks.task_body,
tasks.id as task_id,
lists.list_name,
lists.list_body
'); $this->db->from('tasks'); $this->db->join('lists', 'lists.id = tasks.list_id'); $this->db->where('tasks.list_id',$list_id); if($active == true){ $this->db->where('tasks.is_complete',0); } else { $this->db->where('tasks.is_complete',1); } $query = $this->db->get(); if($query->num_rows() < 1){ return FALSE; } return $query->result(); }

Here we added the second parameter to the method and made the default true. We then set a conditional to see if it was true of false. If false, then we fetched all the tasks that have been completed or are inactive.

Now, we just need to add the foreach for uncompleted tasks in the view under "Recently Completed".

<ul>
<?php foreach($uncompleted_tasks as $task) : ?>
    <li><?php echo $task->task_name; ?></li>
<?php endforeach; ?>
</ul>

Add a Link to The Task

What we want now is to have our task titles link to their repective task show page. In your views/lists/show.php file, add a link to the "show" view of the task (we will create soon)

<li><a href="/<?php echo base_url(); ?>tasks/show/<?php echo $task->task_id; ?>"><?php echo $task->task_name; ?></a></li>

What if there are no tasks? we dont want to have a blank area. So lets add an if statement that says if there are tasks, show the list and if not, show a message. Make your views/lists/show.php file look like this...

<h1><?php echo $list->list_name; ?></h1>
Created on <?php echo $list->create_date; ?>
<br /><br />
<?php echo $list->list_body; ?>
<br /><br />
<h4> Current Open Tasks</h4>
<?php if($completed_tasks) : ?>
    <ul>
    <?php foreach($completed_tasks as $task) : ?>
        <li><a href="/<?php echo base_url(); ?>tasks/show/<?php echo $task->task_id; ?>"><?php echo $task->task_name; ?></a></li>
    <?php endforeach; ?>
    </ul>
<?php else : ?>
    <p>There are no current tasks</p>
<?php endif; ?>
<br />
<h4> Recently Completed</h4>
<?php if($uncompleted_tasks) : ?>
    <ul>
    <?php foreach($uncompleted_tasks as $task) : ?>
        <li><a href="/<?php echo base_url(); ?>tasks/show/<?php echo $task->task_id; ?>"><?php echo $task->task_name; ?></li></a>
    <?php endforeach; ?>
    </ul>
<?php else : ?>
     <p>There are no completed tasks</p>
<?php endif; ?>
<hr />
<a href="/<?php echo base_url(); ?>lists"><- Go Back to Lists</a>

Create a Tasks Controller

Now we need a tasks controller so we can create methods to view, add and edit tasks. Create a file named tasks.php in your controllers folder and add the following...

<?php
class Tasks extends CI_Controller {
    
     public function show($id){
        //Get single task info
        $data['task'] = $this->Task_model->get_task($id);
       
        //Load view and layout
        $data['main_content'] = 'tasks/show';
        $this->load->view('layouts/main',$data);
    }
    
}

Create a Task Model

We have $data['task'] looking for a model method called "get_task($id)". The $id param comes from the url of the task show page. Create a file called task_model.php and place it in the models folder. Add the following...

<?php
class Task_model extends CI_Model{

public function get_task($id){
$this->db->select('*');
$this->db->from('tasks');
$this->db->join('lists', 'lists.id = tasks.list_id','LEFT');
$this->db->where('tasks.id',$id);
$query = $this->db->get();
if($query->num_rows() != 1){
return FALSE;
}
return $query->row();
}

}

This grabs the info/fields for a single task specified in the $id parameter. It also joins the lists table so we can use values like the list name that the task belongs to.

Load the Model

Remember, models need to be loaded. We could load it in the constructor but lets not do that. Lets autoload it. Go to config/autoload.php and look for this line...

$autoload['model'] = array();

and add the list and task model like so..

$autoload['model'] = array('List_model','Task_model');

Since we autoloaded the list model, you can go to controllers/lists.php and get rid of the entire __construct() method as we do not need it to load the list model.

Create Tasks Views

Now ofcourse we need to create the views for tasks. In your views folder, create a folder called "tasks" and in that folder, create the following files..

  • show.php
  • add_task.php
  • edit_task.php

Open up the views/tasks/show.php file and add the following...

<h1><?php echo $task->task_name; ?></h1>
<strong>Created On: </strong><?php echo $task->create_date; ?>
<br /><br />
<strong>Due Date:</strong> <?php echo $task->due_date; ?>
<br /><br />
<div style="max-width:500px;"><?php echo $task->task_body; ?></div>
<br /><hr />
<- Go Back to <a href="/<?php echo base_url(); ?>lists/show/<?php echo $task->list_id; ?>"><?php echo $task->list_name; ?></a>

This will display all info on the task show page

Adding Lists & Tasks

Up to this point, we have taken care of all of the R(read) in CRUD. We now need a way to create lists and tasks. We could just start creating the add and edit files BUT we want this to be a multi-user platform. We need user accounts so people can log in and see their lists and tasks. We will also need to change the models slightly so that each user only sees their own lists and tasks and not all of them in the database. To do this, we need a login system.

Creating a Login System

There are a number of ways to approach this but I like to create the registration functionality first. We need a way to add users and having admins add them through phpmyadmin is definitley not a viable solution. Lets start with the view for the registration form. Create a folder named "users" in the views folder. Now create a file named "register.php". We can use a regular html form here but I would like to use Codeigniter's form helper. If you remember, we added it to the autoload array in the config file. So we should have no issue using it. To learn more about the form helper, visit this page.

Create a User/Register View

<h1>Register</h1>
<p>Please fill out the form below to create an account</p>
<?php echo form_open('users/register'); ?>
<!--Field: First Name-->
<p>
<?php echo form_label('First Name:'); ?>
<?php
$data = array(
              'name'        => 'first_name',
              'value'       => set_value('first_name')
            );
?>
<?php echo form_input($data); ?>
</p>
<!--Field: Last Name-->
<p>
<?php echo form_label('Last Name:'); ?>
<?php
$data = array(
              'name'        => 'last_name',
              'value'       => set_value('last_name')
            );
?>
<?php echo form_input($data); ?>
</p>
<!--Field: Email Address-->
<p>
<?php echo form_label('Email Address:'); ?>
<?php
$data = array(
              'name'        => 'email',
              'value'       => set_value('email')
            );
?>
<?php echo form_input($data); ?>
</p>
<!--Field: Username-->
<p>
<?php echo form_label('Username:'); ?>
<?php
$data = array(
              'name'        => 'username',
              'value'       => set_value('username')
            );
?>
<?php echo form_input($data); ?>
</p>
<!--Field: Password-->
<p>
<?php echo form_label('Password:'); ?>
<?php
$data = array(
              'name'        => 'password',
              'value'       => set_value('password')
            );
?>
<?php echo form_input($data); ?>
</p>
<!--Field: Password2-->
<p>
<?php echo form_label('Confirm Password:'); ?>
<?php
$data = array(
              'name'        => 'password2',
              'value'       => set_value('password2')
            );
?>
<?php echo form_input($data); ?>
</p>
<!--Submit Button-->
<?php $data = array("value" => "Register",
"name" => "submit",
"class" => "btn btn-primary"); ?>
<p>
<?php echo form_submit($data); ?></p> <?php echo form_close(); ?>

This may look a little intimidation at first but it is actually really simple.

  • form_open() - Creates the opening <form> tag and takes the action as a parameter
  • form_label() - Creates a <label> field
  • form_input() - Creates a text <input> field
  • form_password() - Creates a password <input>
  • form_submit() - Creates a submit button
  • form_close() - Creates the closing </form> tag
  • set_value() - This makes it so our fields stay filled even if there is an error

Most of these can take an array of data for things like name, id, class, etc.

Create a Users Controller

Create a file named users.php under controllers and add the following...

<?php
class Users extends CI_Controller{
    
    public function register(){
        $this->form_validation->set_rules('first_name','First Name','trim|required|xss_clean');
        $this->form_validation->set_rules('last_name','Last Name','trim|required|xss_clean');
        $this->form_validation->set_rules('email','Email','trim|required|valid_email|xss_clean');
        
        $this->form_validation->set_rules('username','Username','trim|required|min_length[4]|xss_clean');      
        $this->form_validation->set_rules('password','Password','trim|required|min_length[4]|max_length[50]|xss_clean');
        $this->form_validation->set_rules('password2','Confirm Password','trim|required|matches[password]|xss_clean');
        
        if($this->form_validation->run() == FALSE){
            //Load view and layout
            $data['main_content'] = 'users/register';
            $this->load->view('layouts/main',$data);
        //Validation has ran and passed    
        } else {
           echo 'Youre In!';die();
        }
    }
}

Here is what's happening...We are using the form_validation class which we autoloaded. It uses "set_rules" and takes 3 paramaters. You can read up on the form validation class here.The first param is the name of the field, the second is the human readable name used in error messages. The third param contains all the rules you want to use separated by a | pipe character. We have required fields, email validation, password matching rules and xss_clean which helps prevent cross-site scripting.

Then we are saying if the form has errors, just reload it. If it passes validation, then echo out "Youre In", which ofcourse we will change.

You also need to add the following line to your views/user/register.php file at the top. It will display any errors the form has. You should put it at the top.

<!--Display Errors-->
<?php echo validation_errors('<p class="text-error">'); ?>

Go ahead and try and submit the form with no fields filled in. You should get a bunch of errors

ci12

Fill them all out correctly and you should see "Youre In". We obviously want to change this. We need to add the field values to our users table in our database. In the controllers/users.php file, replace "echo 'Youre In'" with the following...

 if($this->User_model->create_member()){
                $this->session->set_flashdata('registered', 'You are now registered, please log in');
                //Redirect to index page with error above
                redirect('home/index');
 }

Here we are going to call the "create_member" method in the user model (we haven't created yet). This will make a database "insert" and create the user. If that succeeds, we are going to set flashdata. This is a string of text that will be put into memory and is useable in the next page load. So we will redirect to the home/index page with the message. We need to go to views/home.php so we can catch the message and display it. Put this at the top of the file...

<!--Display Messages-->
<?php if($this->session->flashdata('registered')) : ?>
	<?php echo '<p class="text-success">' .$this->session->flashdata('registered') . '</p>'; ?>
<?php endif; ?>

Create the User Model

Go to your config/autoload.php file and load the user model like you did the others. Create a file named user_model.php under the models folder and add the following...

 $new_member_insert = array(
            'first_name'       => $this->input->post('first_name'),
            'last_name'        => $this->input->post('last_name'),
            'email'            => $this->input->post('email'),
            'username'         => $this->input->post('username'),
            'activation_code'  => $activation_code,
            'role'             => 1,
            'password'         => md5($this->input->post('password'))
        );
        
        $insert = $this->db->insert('users', $new_member_insert);
        return $insert;

The above code does an insert from the $_POST fields. It also encrypts the password field with md5. Our register functionality should now be complete. Lets add a "Register" link on the top right of the page. Go to views/layouts/main.php and place the following link under RIGHT TOP CONTENT

	<a href="/<?php echo base_url(); ?>users/register">Register</a>

Now you can easily access the register page

Let's give it a shot. Fill out the register page with valid info. If all is well, you will see a message saying you are registered, you can now login. I realize that it would be nice to send out a confirmational email, etc but that is beyond our scope. You can easily add that functionality as Codeigniter is very scalable. To make sure you got registered, visit phpmyadmin and go to the users table. You can even see that the password is encrypted.

ci13

Create the Login View

So our registration is working flawlessly, but we have no way to login. The login will be a bit different because we want the login form on the side of the page on all pages. So what we can do is create the login view and then include the view into the layout page on the sidebar. So create a file named login.php in the views/users folder and add the following...

<h4>Login Form</h4>
<!--Start Form-->
<?php $attributes = array('id' => 'login_form',
                          'class' => 'form-horizontal'); ?>
<?php echo form_open('user/login',$attributes); ?>
<!--Field: Username-->
<p>
<?php echo form_label('Username:'); ?>
<?php
$data = array(
              'name'        => 'username',
              'placeholder' => 'Enter Username', 
              'style'       => 'width:180px',
              'value'       => set_value('username')
            );
?>
<?php echo form_input($data); ?>
</p>
<!--Field: Password-->
<p>
<?php echo form_label('Password:'); ?>
<?php
$data = array(
              'name'        => 'password',
              'placeholder' => 'Enter Password',
              'style'       => 'width:180px',
              'value'       => set_value('password')
            );
?>
<?php echo form_password($data); ?>
</p>
<br />
<p>
    <!--Submit Button-->
    <?php $data = array("value" => "Login",
                    "name" => "submit",
                    "class" => "btn btn-primary"); ?>
    <?php echo form_submit($data); ?>
</p>
<?php echo form_close(); ?>

Now, go to views/layout/main.php and add this code right under the SIDEBAR CONTENT comment

<?php $this->load->view('users/login'); ?>

Create The login() Method In The Users Controller

This is the same idea as the registration form. We are submitting to the "users" controller and the "login" method. Lets create that now.

In the controllers/users.php, add the this login() method...

public function login(){
        $this->form_validation->set_rules('username','Username','trim|required|min_length[4]|xss_clean');      
        $this->form_validation->set_rules('password','Password','trim|required|min_length[4]|max_length[50]|xss_clean');        
        
        if($this->form_validation->run() == FALSE){
            //Set error
            $this->session->set_flashdata('login_failed', 'Sorry, the login info that you entered is invalid');
            redirect('home/index');
        } else {
           //Get from post
           $username = $this->input->post('username');
           $password = $this->input->post('password');
               
           //Get user id from model
           $user_id = $this->User_model->login_user($username,$password);
               
           //Validate user
           if($user_id){
               //Create array of user data
               $user_data = array(
                       'user_id'   => $user_id,
                       'username'  => $username,
                       'logged_in' => true
                );
                //Set session userdata
               $this->session->set_userdata($user_data);
                                  
               redirect('home/index');
            } else {
                //Set error
                $this->session->set_flashdata('login_failed', 'Sorry, the login info that you entered is invalid');
                redirect('home/index');
            }
        }
    }

So this is the method that runs when we attempt to login. Much like the register method, we run some validation on the username and password fields. If the form is blank, it will set a flash message and redirect or refresh the home/index page. If it is filled out it will then run the "login_user" method in the user_model.php file. This checks to see if there is a user with the specified username and password. If so, it puts the users id into a variable named $user_id. The password is encrypted in the model method which we will look at next.

So if there is a $user_id, we then create an array named $user_data, which holds the id, username and a "logged_in" field which will be set to 1 on a successful login. Lastly, we create a session and add this data to the session. using $this->session->set_userdata($user_data). So now we can access this session array and check if the user is logged in and display different data depending on the user's state.

Message Output

Go to your views/users/login.php file and add the flash message code under your h1 heading...

<?php if($this->session->flashdata('login_failed')) : ?>
    <?php echo '<p class="text-error">' .$this->session->flashdata('login_failed') . '</p>'; ?>
<?php endif; ?>

The login_user() Method

Now open up your user_model.php file and add the login_user method with the username and password parameters like so...

public function login_user($username,$passowrd){
        //Secure password
        $enc_password = md5($passowrd);
        
        //Validate
        $this->db->where('username',$username);
        $this->db->where('password',$enc_password);
        
        $result = $this->db->get('users');
        if($result->num_rows() == 1){
            return $result->row(0)->id;
        } else {
            return false;
        }
    }

The first thing we do is encrypt the password. Remember all of our passwords have md5 encryption so we need to convert our entered password to md5 so that it will match the one in the database.

Next we include where clauses for the username and the password. We put the result in a variable and check to see if the number of rows returned is equal to 1. If so, then there is a match. We then return the user id with return $result->row(0)->id; If there is no user, we return false.

Now you can try logging in. Right now, there is no way to tell you are logged in except that you will not get an error message. Lets look at the "Register" link in the top right of your site. Lets make it so it shows Register if not logged in and shows a welcome message if we are logged in. To do this, go to views/layouts/main.php and look for the register link line. It should read...

 <a href="/<?php echo base_url(); ?>users/register">Register</a>

Change this to...

<?php if($this->session->userdata('logged_in')) : ?>
      Welcome,  <?php echo $this->session->userdata('username'); ?>
<?php else : ?>
      <a href="/<?php echo base_url(); ?>users/register">Register</a>
<?php endif; ?>

Now if you are logged in, you should see a welcome message

ci14

Log Out

Now lets create a logout function. Open controllers/users.php and add this method...

public function logout(){
        //Unset user data
        $this->session->unset_userdata('logged_in');
        $this->session->unset_userdata('user_id');
        $this->session->unset_userdata('username');
        $this->session->sess_destroy();
        
         //Set message
        $this->session->set_flashdata('logged_out', 'You have been logged out');
        redirect('home/index');
}
    

This is actually very simple. What we are doing here is unsetting all $user_data array values (username, password and logged_in), destroying the session and redirecting to the homepage with a logged out message. Lets add that message to views/home.php

    <?php if($this->session->flashdata('logged_out')) : ?>
    <?php echo '<p class="text-success">' .$this->session->flashdata('logged_out') . '</p>'; ?>
<?php endif; ?>

Another thing we want to do is remove the username and password input fields when logged in, and instead put a login button. Change the code in views/users/login.php to the following...

<?php if($this->session->userdata('logged_in')) : ?>
    <p>You are logged in as <?php echo $this->session->userdata('username'); ?></p>
    <!--Start Form-->
    <?php $attributes = array('id' => 'logout_form',
                          'class' => 'form-horizontal'); ?>
    <?php echo form_open('users/logout',$attributes); ?>
         <!--Submit Buttons-->
    <?php $data = array("value" => "Logout",
                    "name" => "submit",
                    "class" => "btn btn-primary"); ?>
    <?php echo form_submit($data); ?>
    <?php echo form_close(); ?>
<?php else : ?>
    <h4>Login Form</h4>
	<?php if($this->session->flashdata('login_failed')) : ?>
    	<?php echo '<p class="text-error">' .$this->session->flashdata('login_failed') . '</p>'; ?>
	<?php endif; ?>
	<!--Start Form-->
	<?php $attributes = array('id' => 'login_form',
                          'class' => 'form-horizontal'); ?>
	<?php echo form_open('users/login',$attributes); ?>
	<!--Field: Username-->
	<p>
	<?php echo form_label('Username:'); ?>
	<?php
	$data = array(
              'name'        => 'username',
              'placeholder' => 'Enter Username', 
              'style'       => 'width:180px',
              'value'       => set_value('username')
            );
	?>
	<?php echo form_input($data); ?>
	</p>
	<!--Field: Password-->
	<p>
	<?php echo form_label('Password:'); ?>
	<?php
	$data = array(
              'name'        => 'password',
              'placeholder' => 'Enter Password',
              'style'       => 'width:180px',
              'value'       => set_value('password')
            );
	?>
	<?php echo form_password($data); ?>
	</p>
	<br />
	<p>
    <!--Submit Buttons-->
    <?php $data = array("value" => "Login",
                    "name" => "submit",
                    "class" => "btn btn-primary"); ?>
    <?php echo form_submit($data); ?>
	</p>
	<?php echo form_close(); ?>
<?php endif; ?>

What we have done with the above code is set a conditional saying that if we are logged in, show a welcome message and a logout button that calls the users/logout method. If we are not logged in, then show the original form.

Personalize Lists

As our application is now, anyone can login and see all of the lists and their tasks, even users that are not logged in. Obviously this is not what we want. We want users to only see their own lists. This just requires some tweaking of our code. Lets first make it so that guests can not see the lists page. Go to controllers/lists.php and create a constructor method which will check if the user is logged in and if not, redirect to the homepage with a message.

 public function __construct() {
        parent::__construct();
        if(!$this->session->userdata('logged_in')){
            //Set error
            $this->session->set_flashdata('need_login', 'Sorry, you need to be logged in to view that area');
            redirect('home/index');
        }
    }

Now add the message to views/home.php under the other messages...

<?php if($this->session->flashdata('need_login')) : ?>
    <?php echo '<p class="text-error">' .$this->session->flashdata('need_login') . '</p>'; ?>
<?php endif; ?>

Lets go a step further and hide the "Lists" menu link unless we are logged in. Go to views/layouts/main.php and add the following to replace the original "Lists" link...

<?php if($this->session->userdata('logged_in')) : ?>
       <li><a href="/<?php echo base_url(); ?>lists">My Lists</a></li>  
<?php endif; ?>

Ok, now we just need to make it so the user only sees their own lists. Go to controllers/lists.php and replace the index() method's content to this...

public function index(){
        //Get the logged in users id
        $user_id = $this->session->userdata('user_id');
        //Get all lists from the model
        $data['lists'] = $this->List_model->get_all_lists($user_id);
        
        //Load view and layout
        $data['main_content'] = 'lists/index';
        $this->load->view('layouts/main',$data);
}

We are now getting the user id and passing it to the get_all_lists() method in the model. Now open up models/list_model.php and change the get_all_lists() method to the following...

public function get_all_lists($user_id){
        $this->db->where('list_user_id',$user_id);
        $query = $this->db->get('lists');
        return $query->result();
}

So that should do it, but how can we test if all lists belong to the user with an id of 1? We can fix this by going into phpmyadmin. Go to the "lists" table and select one of the entries and change the list_user_id to "2". Now visit the lists page and you should only see 2 entries.

Adding Lists and Tasks

We are just about there. We just need a way to add lists and tasks. Let's start by creating the form to add a list. Go to views/lists/add_list.php and add the form...

<h1>Add a List</h1>
<p>Please fill out the form below to create a new task list</p>
<!--Display Errors-->
<?php echo validation_errors('<p class="text-error">'); ?>
 <?php echo form_open('lists/add'); ?>
<!--Field: First Name-->
<p>
<?php echo form_label('List Name:'); ?>
<?php
$data = array(
              'name'        => 'list_name',
              'value'       => set_value('list_name')
            );
?>
<?php echo form_input($data); ?>
</p>
<!--Field: Last Name-->
<p>
<?php echo form_label('List Body:'); ?>
<?php
$data = array(
              'name'        => 'list_body',
              'value'       => set_value('list_body')
            );
?>
<?php echo form_textarea($data); ?>
</p>
<!--Submit Buttons-->
<?php $data = array("value" => "Add List",
                    "name" => "submit",
                    "class" => "btn btn-primary"); ?>
<p>
    <?php echo form_submit($data); ?>
</p>
<?php echo form_close(); ?>

Now lets create the link to add a list. Go to views/lists/index.php and create the link at the bottom...

<p>To create a new list - <a href="/<?php echo base_url(); ?>lists/add">Click here</a>

Next we need to create the add() method in controllers/lists.php

public function add(){
        $this->form_validation->set_rules('list_name','List Name','trim|required|xss_clean');
        $this->form_validation->set_rules('list_body','List Body','trim|xss_clean');
        
        if($this->form_validation->run() == FALSE){
            //Load view and layout
            $data['main_content'] = 'lists/add_list';
            $this->load->view('layouts/main',$data);  
        } else {
             //Post values to array
            $data = array(             
                'list_name'    => $this->input->post('list_name'),
                'list_body'    => $this->input->post('list_body'),
                'list_user_id' => $this->session->userdata('user_id')
            );
           if($this->List_model->create_list($data)){
                $this->session->set_flashdata('list_created', 'Your task list has been created');
                //Redirect to index page with error above
                redirect('lists/index');
           }
        }
}
    

So the add method works like any form submission method (eg. register, login, etc). It runs validation. If validation passes, we put the 2 post variables from the form as well as the session user_id variable into an array called $data. We then submit that data to the model function "create_list()".

Before we create that model method, lets add the flash message to lists/index. Open up views/lists/index.php and add this to the top...

<?php if($this->session->flashdata('list_created')) : ?>
    <?php echo '<p class="text-success">' .$this->session->flashdata('list_created') . '</p>'; ?>
<?php endif; ?>
    

Model Method - create_list()

Open up models/list_model.php and add this simple method...

public function create_list($data){
	$insert = $this->db->insert('lists', $data);
	return $insert;
}   

So now you should be able to login and go to mytodo/lists/add and add a new list. It will be added to the database.

Deleting Lists

We need a way to delete lists. Go to the views/lists/show.php file. Add the following line to the very top of the page above the h1 heading...

<a class="delete_list" onclick="return confirm('Are you sure?')" href="/<?php echo base_url(); ?>lists/delete/<?php echo $list->id; ?>">Delete This List</a>

This link asks to confirm the delete, then calls the "delete()" method in the lists controller, so lets create that...

	public function delete($list_id){      
            //Delete list
            $this->List_model->delete_list($list_id);
            //Create Message
            $this->session->set_flashdata('list_deleted', 'Your list has been deleted');         
            //Redirect to list index
            redirect('lists/index');
     }

This is a simple method. It basically justs calls the delete_list() method of the model. It passes the list id so that it knows which list to delete. So lets now add the delete_list() method to the models/list_model.php file.

public function delete_list($list_id){
        $this->db->where('id',$list_id);
        $this->db->delete('lists');
        $this->delete_tasks_of_list($list_id);
        return;
}

Lets take a look at the code above. We are forming a query we want to get all fields from the lists table where the list has the id of the $list_id parameter. Then we delete the list. We are not done though..You know that tasks belong to lists. They have a many to one relationship. All tasks have a list_id and if that list_id is deleted, then so should all the tasks in the list. That is where the line $this->delete_tasks_of_list($list_id); comes in. We are now calling another method in the same class called delete_tasks_of_list(). So lets create this right below the delete_list() method.

private function delete_tasks_of_list($list_id){
     $this->db->where('list_id',$list_id);
     $this->db->delete('tasks');
}

Notice that this method is "private". That is because we are calling it in the same class. in the function above. In this method we are simply deleting all tasks with the list_id of the list we are deleting.

Adding Tasks To a List

Now we need a form to add a task to a list. Lets start with the add task form. Open up views/tasks/add_task.php. Add the following...

<h1>Add a Task</h1>
<p>List:<strong> <?php echo $list_name; ?></strong></p>
<!--Display Errors-->
<?php echo validation_errors('<p class="text-error">'); ?>
<?php echo form_open('tasks/add/'.$this->uri->segment(3).''); ?>
<!--Field: Task Name-->
<p>
<?php echo form_label('Task Name:'); ?>
<?php
$data = array(
              'name'        => 'task_name',
              'value'       => set_value('task_name')
            );
?>
<?php echo form_input($data); ?>
</p>
<!--Field: Task Body-->
<p>
<?php echo form_label('Task Body:'); ?>
<?php
$data = array(
              'name'        => 'task_body',
              'value'       => set_value('task_body')
            );
?>
<?php echo form_textarea($data); ?>
</p>
<!--Field: Date-->
<p>
<?php echo form_label('Date:'); ?>
<input type="date" name="due_date" />
</p>
<!--Submit Buttons-->
<?php $data = array("value" => "Add Task",
                    "name" => "submit",
                    "class" => "btn btn-primary"); ?>
<p>
    <?php echo form_submit($data); ?>
</p>
<?php echo form_close(); ?>

We pretty much have seen all of this. One thing new in this form is in the form_open() method, after the tasks/add/ we use the $this->uri->segment(3) helper method to grab the list id from the url. The "3" is because it is the third value in the url (eg. tasks/add/1). THis id number is passed to the tasks/add method as the $list_id parameter,

Lets create a link to the form in the views/lists/show.php file. Replace the first line which holds the delete link to the following...

<ul id="actions">
<h4>List Actions</h4>
<li> <a href="/<?php echo base_url(); ?>tasks/add/<?php echo $list->id; ?>">Add Task</a></li>
<li> <a href="/<?php echo base_url(); ?>lists/edit/<?php echo $list->id; ?>">Edit List</a></li>
<li> <a onclick="return confirm('Are you sure?')" href="/<?php echo base_url(); ?>lists/delete/<?php echo $list->id; ?>">Delete List</a></l
</ul>

Now lets add a little style to the action links. Go to assets/css/custom.css and add the following...

ul#actions{
    padding:0;
    margin:0;
    float:right;
    width:180px;
    padding:5px;
    background:#f4f4f4;
    border:#ccc 1px solid;
}
ul#actions li{
    padding:2px 0 2px 15px;
    margin:0;
    list-style:none;
}

Now you should have a grey box on the right with add and delete options

ci15

Add The add() Method In The Controller

Open up controllers/tasks.php and add the add() method..

 public function add($list_id = null){
        $this->form_validation->set_rules('task_name','Task Name','trim|required|xss_clean');
        $this->form_validation->set_rules('task_body','Task Body','trim|xss_clean');       
        if($this->form_validation->run() == FALSE){
            //Get list name for view
            $data['list_name'] = $this->Task_model->get_list_name($list_id); 
            //Load view and layout
            $data['main_content'] = 'tasks/add_task';
            $this->load->view('layouts/main',$data);  
        } else {
             //Post values to array
            $data = array(             
                'task_name'    => $this->input->post('task_name'),
                'task_body'    => $this->input->post('task_body'),
                'due_date'     => $this->input->post('due_date'),
                'list_id'      => $list_id
            );
           
           if($this->List_model->create_task($data)){
                $this->session->set_flashdata('task_created', 'Your task has been created');
                //Redirect to index page with error above
                redirect('lists/show/'.$list_id.'');
           }
       }
 }

We first do our validations, then if they pass, we put all post data into an array named $data and we pass that to the model method of "create_task()". f the task is created, we get a message and redirected back to the list page.

Add create_task()

This method is very simple. We just need to pass in the data and insert into the tasks table.

public function create_task($data){
	$insert = $this->db->insert('tasks', $data);
	return $insert;
}
    

We also need to add the get_list_name() method to the task_model.php file

public function get_list_name($list_id){
        $this->db->where('id',$list_id);
        $query = $this->db->get('lists');
        return $query->row()->list_name;
}

Now you should be able to add tasks to your lists.

Adding Delete Task Functionality

Lets start with creating a link to delete a task. Lets create a task actions box like we did with the lists. Place this at the very top of your views/tasks/show.php file.

<ul id="actions">
<h4>Task Actions</h4>
<li> <a href="/<?php echo base_url(); ?>tasks/add/<?php echo $task->list_id; ?>">Add Task</a></li>
<li> <a href="/<?php echo base_url(); ?>tasks/edit/<?php echo $task->id; ?>">Edit Task</a></li>
<li> <a onclick="return confirm('Are you sure?')" href="/<?php echo base_url(); ?>tasks/delete/<?php echo $task->list_id; ?>/<?php echo $this->uri->segment(3); ?>">Delete Task</a></li>
</ul>

The link in the code above is passing the list id and the task id to the delete() method of the tasks controller. The only reason the list id is getting passed is so we can set a redirect to that particular list page after we delete the task.

Now lets create the delete() method in controllers/tasks.php

public function delete($list_id,$task_id){    
       //Delete list
       $this->Task_model->delete_task($task_id);
       //Create Message
       $this->session->set_flashdata('task_deleted', 'Your task has been deleted');        
       //Redirect to list index
       redirect('lists/show/'.$list_id.'');
}
    

So in the code abover we are calling the delete_task() method in the tasks model. Lets create that method now. Open up models/task_model.php and add the method..

public function delete_task($task_id){
        $this->db->where('id',$task_id);
        $this->db->delete('tasks');
        return;
}

So all we need to do now is add the message to the views/lists/show.php file. Place the following code right under the h1 heading...

<?php if($this->session->flashdata('task_created')) : ?>
    <?php echo '<p class="text-success">' .$this->session->flashdata('task_created') . '</p>'; ?>
<?php endif; ?>

At this point, you should be able to add and delete both lists and tasks. Now we need to deal with the U in "CRUD", which is update (or edit). Lets first create the edit list functionality. Lets start with the edit form. Open up views/lists/edit_list.php and add the following...

<h1>Edit List</h1>
<!--Display Errors-->
<?php echo validation_errors('<p class="text-error">'); ?>
 <?php echo form_open('lists/edit/'.$this_list->id.''); ?>
<!--Field: First Name-->
<p>
<?php echo form_label('List Name:'); ?>
<?php
$data = array(
              'name'        => 'list_name',
              'value'       => $this_list->list_name
            );
?>
<?php echo form_input($data); ?>
</p>
<!--Field: Last Name-->
<p>
<?php echo form_label('List Body:'); ?>
<?php
$data = array(
              'name'        => 'list_body',
              'value'       => $this_list->list_body
            );
?>
<?php echo form_textarea($data); ?>
</p>
<!--Submit Buttons-->
<?php $data = array("value" => "Update List",
                    "name" => "submit",
                    "class" => "btn btn-primary"); ?>
<p>
    <?php echo form_submit($data); ?>
</p>
<?php echo form_close(); ?>

The code above is submiting a form to controllers/lists/edit/(the list id) lets look at that file and create the edit() method...

public function edit($list_id){
        $this->form_validation->set_rules('list_name','List Name','trim|required|xss_clean');
        $this->form_validation->set_rules('list_body','List Body','trim|xss_clean');
        
        if($this->form_validation->run() == FALSE){
            //Get the current list info
            $data['this_list'] = $this->List_model->get_list_data($list_id);
            //Load view and layout
            $data['main_content'] = 'lists/edit_list';
            $this->load->view('layouts/main',$data);  
        } else {
            //Validation has ran and passed  
             //Post values to array
            $data = array(             
                'list_name'    => $this->input->post('list_name'),
                'list_body'    => $this->input->post('list_body'),
                'list_user_id' => $this->session->userdata('user_id')
            );
           if($this->List_model->edit_list($list_id,$data)){      
                $this->session->set_flashdata('list_updated', 'Your task list has been updated');
                //Redirect to index page with error above
                redirect('lists/index');
           }
       }

You may have noticed the line

$data['this_list'] = $this->List_model->get_list_data($list_id);

. This lets use use the info of the selected list in our view. That way, we can insert the current values into the form.

After the form validation, we go ahead and place the post form variables into an array named $data. We then call the edit_list() model in the lists_model.php file.

public function edit_list($list_id,$data){
        $this->db->where('id', $list_id);
        $this->db->update('lists', $data); 
        return TRUE;
}

This simply updates the specified fields

So lets add the message to the views/lists/show.php file. Put it right under the others...

<?php if($this->session->flashdata('list_updated')) : ?>
    <?php echo '<p class="text-success">' .$this->session->flashdata('list_updated') . '</p>'; ?>
<?php endif; ?>

Editing Tasks

Now we need a way to edit tasks. So lets start with the edit form. Go to views/tasks/edit_tasks.php

>h1>Edit Task</h1>
<p>List:<strong> <?php echo $list_name; ?></strong></p>
<!--Display Errors-->
<?php echo validation_errors('<p class="text-error">'); ?>
<?php echo form_open('tasks/edit/'.$this->uri->segment(3).''); ?>
<!--Field: Task Name-->
<p>
<?php echo form_label('Task Name:'); ?>
<?php
$data = array(
              'name'        => 'task_name',
              'value'       => $this_task->task_name
            );
?>
<?php echo form_input($data); ?>
</p>
<!--Field: Task Body-->
<p>
<?php echo form_label('Task Body:'); ?>
<?php
$data = array(
              'name'        => 'task_body',
              'value'       => $this_task->task_body
            );
?>
<?php echo form_textarea($data); ?>
</p>
<!--Field: Date-->
<p>
<?php echo form_label('Date:'); ?>
<input type="date" name="due_date" value="<?php echo $this_task->due_date; ?>"/>
</p>
<!--Submit Buttons-->
<?php $data = array("value" => "Update Task",
                    "name" => "submit",
                    "class" => "btn btn-primary"); ?>
<p>
    <?php echo form_submit($data); ?>
</p>
<?php echo form_close(); ?>

The only thing to really notice, is the values. These are being passed to the view from the tasks edit method.

Its pointing to tasks/edit/'.$this->uri->segment(3). The segment is the id of the task. Lets build the task controller method of "edit()"

 public function edit($task_id){
        $this->form_validation->set_rules('task_name','Task Name','trim|required|xss_clean');
        $this->form_validation->set_rules('task_body','Task Body','trim|xss_clean');       
        if($this->form_validation->run() == FALSE){
            //Get list id
            $data['list_id'] = $this->Task_model->get_task_list_id($task_id);
            //Get list name for view
            $data['list_name'] = $this->Task_model->get_list_name($data['list_id']); 
            //Get the current task info
            $data['this_task'] = $this->Task_model->get_task_data($task_id);
            //Load view and layout
            $data['main_content'] = 'tasks/edit_task';
            $this->load->view('layouts/main',$data);  
        } else {
            //Get list id
            $list_id = $this->Task_model->get_task_list_id($task_id);
            //Post values to array
            $data = array(             
                'task_name'    => $this->input->post('task_name'),
                'task_body'    => $this->input->post('task_body'),
                'due_date'     => $this->input->post('due_date'),
                'list_id'      => $list_id
            );
           if($this->Task_model->edit_task($task_id,$data)){
                $this->session->set_flashdata('task_updated', 'Your task has been updated');
                //Redirect to index page with error aboves
                redirect('lists/show/'.$list_id.'');
           }
        }
    }
    

Here, we are setting form falidation. We are creating an array value for "list_id" and "list_name", so we can access these in the view. If all goes well, then the task info is updated in the database, then it redirects to the lists/show page with a message. Lets add the message to views/lists/show.php

<?php if($this->session->flashdata('task_updated')) : ?>
    <?php echo '<p class="text-success">' .$this->session->flashdata('task_updated') . '</p>'; ?>
<?php endif; ?>
    

We have a couple small methods to create in the task_model.php file. Add these to that file...

     public function get_task_list_id($task_id){
        $this->db->where('id',$task_id);
        $query = $this->db->get('tasks');
        return $query->row()->list_id;
    }
    
    

Now lets create the model method edit_task(). This takes 2 parameters, $task_id and $data (which is the data in the array we set.). In your models/task_model, create the edit_task() method.

 public function edit_task($task_id,$data){
     $this->db->where('id', $task_id);
     $this->db->update('tasks', $data); 
     return TRUE;
 }
    

You should now be able to create , read, update and delete lists and tasks.

Homepage View

When the user logs in I want them to see the most recent lists along with the recent tasks in some kind of table. So lets open up the controllers/home.php file. Lets get rid of the data array with the heading $data = array('welcome_heading' => 'Welcome to myTodo!'); and also go to views/home.php and replace <h1><?php echo $welcome_heading; ?></h1> with <h1>Welcome to myTodo!</h1>.

Now lets get back to adding the tables to our logged in home view. First, we want to get 3 things. The user_id from the session array, the user's lists and the user's tasks. BUT we only want this if the user is logged in. So add this to your home/index method...

if($this->session->userdata('logged_in')){
     //Get the logged in users id
     $user_id = $this->session->userdata('user_id');
     //Get all lists from the model
     $data['lists'] = $this->List_model->get_all_lists($user_id);
     $data['tasks'] = $this->Task_model->get_users_tasks($user_id);
}    

The code above is getting the users lists and tasks from the model. The first model method it calls is $this->List_model->get_all_lists($user_id). This is already existing so no need to create it. The next one which is $this->Task_model->get_users_tasks($user_id), we need to create. So go to models/task_model.com and create the following method...

	 public function get_users_tasks($user_id){
        $this->db->where('list_user_id',$user_id);
        $this->db->join('tasks', 'lists.id = tasks.list_id');
        $this->db->order_by('tasks.create_date', 'asc'); 
        $query = $this->db->get('lists');
        return $query->result();
    }

This method gets all tasks that belong to the user. We first need to look at the lists of the user, then we look at all tasks from those lists.

The Home View

Go to views/home.php and replace everything with the following...

	<!--Display Messages-->
<?php if($this->session->flashdata('registered')) : ?>
    <?php echo '<p class="text-success">' .$this->session->flashdata('registered') . '</p>'; ?>
<?php endif; ?>
<?php if($this->session->flashdata('logged_out')) : ?>
    <?php echo '<p class="text-success">' .$this->session->flashdata('logged_out') . '</p>'; ?>
<?php endif; ?>
<?php if($this->session->flashdata('need_login')) : ?>
    <?php echo '<p class="text-error">' .$this->session->flashdata('need_login') . '</p>'; ?>
<?php endif; ?>
<h1>Welcome to myTodo</h1>
<p>myTodo us a simple and helpful application to help you manage your day to day tasks. You can add as many task lists as you want and as many tasks as you want. myTodo is absolutely free! Have fun.</p>
<?php if($this->session->userdata('logged_in')) : ?>
<h3>My Latest Lists</h3>
<table class="table table-striped" width="50%" cellspacing="5" cellpadding="5">
    <tr>
        <th>List Name</th>
        <th>Created On</th>
        <th>View</th>
    </tr>
    <?php foreach($lists as $list) : ?>
    <tr>
        <td><?php echo $list->list_name; ?></td>
        <td><?php echo $list->create_date; ?></td>
        <td><a href="/<?php echo base_url(); ?>lists/show/<?php echo $list->id; ?>">View List</a></td>
    </tr>
    <?php endforeach; ?>
</table>
<h3>Latest Tasks</h3>
<table class="table table-striped" width="50%" cellspacing="5" cellpadding="5">
    <tr>
        <th>Task Name</th>
			<th>List Name</th>
        <th>Created On</th>
        <th>View</th>
    </tr>
<?php foreach($tasks as $task) : ?>
     <tr>
        <td> <?php echo $task->task_name; ?></td>
		<td> <?php echo $task->list_name; ?></td>
        <td><?php echo $task->create_date; ?></td>
        <td><a href="/<?php echo base_url(); ?>tasks/show/<?php echo $task->id; ?>">View Task</a></td>
    </tr>
<?php endforeach; ?>
</table>
<?php endif; ?>

Now you should see a nicely formatted table of lists and tasks on your homepage when logged in.

ci16

Date Formatting

The dates in this app are not very readable. So lests clean them up a bit...

Open up views/lists/show.php and look for this line...

  Created on: <?php echo $list->create_date; ?>

Replace this line with...

  Created on:  <strong><?php echo date("n-j-Y",strtotime($list->create_date)); ?></strong>

Now open up views/tasks/show.php and change

<strong>Created On: </strong><?php echo $task->create_date; ?>

to this line...

Due Date: <strong><?php echo date("n-j-Y",strtotime($task->create_date)); ?></strong>

This just makes the date a little more readable. If you want more on formatting dates with PHP, go here. You may want to use the name of the day of the week or something. You can learn how to do that at the php.net link.

Conclusion

So that's it! You have built your very own task manager application in PHP. The system uses user authentication and can handle as many users as you want, all with theri very own lists and tasks. Codeigniter is very expandable so you can add more functionality if you want. I will be making a video series on this same project as well.

Resources

About The Author

Brad Traversy is a web developer/designer from Boston Massachusetts. He specializes in web development using open source content management systems such as Joomla, Drupal and Wordpress.

Latest News