Simple Blog – Example 8: CodeIgniter

Simple Blog is a multi-part series. Check out The Index

In example 4, we implemented the MVC pattern for our Simple Blog app. We physically separated our data, presentation, and application logic into models, views, and controllers. In example 6, we rewrote our data logic using PDO which enabled parameterized statements and database portability. In example 7, we implemented the Smarty Template Engine to cleanup our views with a more readable syntax.

So, we’ve developed a web application written in PHP5 that follows MVC, supports multiple databases, provides object-record mapping, has a built-in template engine, and built-in caching. Many of the features we’ve implemented for Simple Blog are precisely the features offered by PHP Frameworks. So, the next logical step was to rewrite the app using a PHP Framework such as CakePHP, CodeIgniter, Symfony, Yii, or Zend which promise to deliver all of the features we’ve implemented so far and more.

In the process of researching these frameworks, I ran across CodeIgniter’s User Guide which was so well written and so well organized that I decided to start with CodeIgniter. A quick glance at codeigniter.com and we get to the promises: small footprint, exceptional performance, nearly zero configuration, and clear documentation. Well, let’s dive in and see what CodeIgniter is all about.

Installation

I’ve played around with CakePHP, CodeIgniter, Yii, and Django. Of the four frameworks, CodeIgniter is the easiest to install. To install CodeIgniter download the source files, unzip the package, and move the folder to a directory accessible by your web server. Next, open application/config/config.php and set your application’s base URL. Finally, open application/config/database.php and configure your connectivity settings. That’s it, CodeIgniter is installed and we’re ready to write some code.

Configuration

First, configure base URL in application/config/config.php:

$config['http://BASE_URL']	= 
	"http://localhost/~Graeson/php/apps/blog/8-codeigniter/";

Next, configure database connectivity in application/config/database.php:

$db['default']['hostname'] = "localhost";
$db['default']['username'] = "blog";
$db['default']['password'] = "secret";
$db['default']['database'] = "blog";
$db['default']['dbdriver'] = "mysql";

Next, our application will have a single controller named blog, so let’s go ahead and set the default controller to ‘blog’ in application/config/routes.php:

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

By default, we would access our application using URLs such as http://baseurl/index.php/blog/index or http://baseurl/index.php/blog/create. Notice how CodeIgniter’s front controller named index.php is listed before our own blog controller. Let’s remove index.php from our URLs by configuring CodeIgniter and creating an .htaccess file in our application’s root directory.

First, open application/config/config.php and set:

$config['index_page'] = "";

Next, create a file named .htaccess in your application’s root:

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteBase /
    RewriteCond %{REQUEST_URI} ^system.*
    RewriteRule ^(.*)$ /index.php?/$1 [L]   
    RewriteCond %{REQUEST_URI} ^application.*
    RewriteRule ^(.*)$ /index.php?/$1 [L]
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^(.*)$ index.php?/$1 [L]
</IfModule>

<IfModule !mod_rewrite.c>
    ErrorDocument 404 /index.php
</IfModule>

Finally, update RewriteBase in your .htaccess file. Set RewriteBase equal to your base URL minus the leading http://localhost or http://example.com. My base URL is:

http://localhost/~Graeson/php/apps/blog/8-codeigniter/

… so, my RewriteBase is:

RewriteBase /~Graeson/php/apps/blog/8-codeigniter/

Now we can access our application using URLs such as http://baseurl/blog/index or http://baseurl/blog/create. Notice that our index page can actually be accessed two other ways. If no method is specified in the URL, CodeIgniter defaults to the specified controller’s index method, so, http://baseurl/blog/ will also route us to our index page. Also, remember that we set ‘blog’ as the default controller so http://baseurl/ will also route us to our index page.

Models

In simplistic terms, Models are PHP classes designed to interact with an application’s data source. In our case, the data source is a MySQL database named blog and our model class will contain methods to create, read, update, and delete said database. CodeIgniter uses the active record pattern which wraps database tables or views into a class. Thus, an object instance is tied to a single row in the table. This pattern allows us to simplify database interaction as well as create database agnostic applications.

To create a new model using CodeIgniter we’ll start out with a standard class definition for a class named Post_model. You’ll see that in function, our new Post_model class is near identical to the Post model we created and used in Examples 3-7. The difference lives in the syntax. In our new Post_model we’ll be using CodeIgniter’s active record database pattern to interact with the database.

class Post_model extends Model
{		
	public $id;
	public $title;
	public $content;
	public $created;
	
	public function __construct() 
	{
		parent::Model();
	}

	public function getAll()
	{
		$query = $this->db->get('post');
		return $query->result();
	}
	
	public function getById($id) 
	{
		$query = $this->db->get_where('post', array('id' => $id));
		return $query->row();
	}

	private function insert($post) 
	{
		return $this->db->insert('post', $this);
	}
	
	private function update($post) 
	{
		$this->db->set('title', $this->title);
		$this->db->set('content', $this->content);
		$this->db->where('id', $this->id);
		return $this->db->update('post');
	}

	public function delete() 
	{
		$this->db->where('id', $this->id);
		return $this->db->delete('post');
	}
	
	public function save() 
	{
		if (isset($this->id)) {	
			return $this->update();
		} else {
			return $this->insert();
		}
	}
}
  • To query the database we use get(‘table’) for full table scans and get_where(‘table’, ‘condition’) for filtered scans. We retrieve a result set using result() and a single row using row().
  • To perform data manipulation we use insert(), update(), delete().

Now, what we don’t see:

  • No database connections strings.
  • No opening and closing of database connections.
  • No selecting of target databases.
  • No building sql or dml statements.
  • No data escaping or sanitation.
  • No binding parameters to statements.
  • No looping through cursors to fetch data.

Needless to say, CodeIgniter has greatly simplified our database operations.

Views

There’s really not much to discuss here. If you’ve been following along, the views we’ll use for CodeIgniter are nearly identical to the views we used in Examples 4-6. I’ll highlight the differences below:

List View

<?php foreach($posts as $post) : ?>
	
	<h4>
		<?php echo anchor("blog/read/{$post->id}", $post->title); ?>
	</h4>
	
	<p>
		<?php echo $post->content; ?>
		<?php echo $post->created; ?>
	</p>
	
<?php endforeach; ?>
  • We’re using CodeIgniter’s anchor function to build our title anchor

Read View

<h3>
	<?php echo $post->title; ?>
</h3>

<p>
	<?php echo $post->content; ?>&nbsp
	<?php echo $post->created; ?><br />
	<?php echo anchor('blog/update/'.$post->id, 'Update'); ?>&nbsp
	<?php echo anchor('blog/delete/'.$post->id, 'Delete', 
		array('onclick' => "return confirm
			('Are you sure want to delete this post?')")); ?>
</p>
  • We’re using CodeIgniter’s anchor function to build our update and delete links. Also, notice that we’re passing javascript to the anchor function for the delete link.

Upsert View

<?php echo form_open($action) ;?>

	<p>	
		<label for="title">Title</label><br />
		<input id="title" name="title" type="text" 
			size="75" value="<?php echo $title; ?>" />
	</p>

	<p>
		<label for="content">Content</label><br />
		<textarea id="content" name="content"
			><?php echo $content; ?></textarea>	
	</p>

	<p>
		<input type="submit" />
	</p>
	
</form>
  • We’re using CodeIgniter’s form_open function to start the HTML form. The form_open function dynamically builds action paths using the base URL we configured earlier. In the event that our base URL were to change, the form_open would dynamically build a new action path.
  • Notice that we’re passing a variable named $action from the controller. If this view is called by the controller’s create method, action will bet set to ‘blog/create’. If this view is called by the controller’s update method, action will bet set to ‘blog/update/id’.

Controllers

In example 4, we extracted all data and presentation logic from our base pages. The only code left in our base pages was application logic. Our base pages became simply traffic controllers which received the initial request, called the model for any database interaction, and called the appropriate view for any content display.

Conceptually, CodeIgniter’s controllers are no different. The difference is that now our application logic will be stored in methods rather than pages. In other words, instead of having a physical page named index.php that makes one call to the model and one call to the index view, we’ll have a method called index() within a class called Blog that will perform that same tasks.

Here’s what our controller looked like when our application logic was all stored in a file called index.php:

// Get posts from database
$posts = Post::getAll();

// Include page view
require_once(VIEW_PATH.'index.view.php');

And here’s what our controller looks like now that our application logic is all stored in a class method:

public function index()
{	
	// Get posts from database
	$data['posts'] = $this->post->getAll();
	
	// Load page view
	$this->load->view('index', $data);
}

As you can see, the tasks performed are identical:

  1. Get a listing of posts from the database.
  2. Load a view to display content to the user.

The key difference between the two controllers is how they send dynamic data to views. In the first controller, we include the view file at runtime. So, any variables created in the controller are accessible by the view. In the second controller, however, we build up an array of data called $data with dynamic data to be displayed by the view at runtime. We then pass the $data array as the second parameter of the view loading function. For example, $this->load->view(‘index’ $data). Behind the scenes, CodeIgniter processes the array and converts each key/value into a variable. For example, $data[‘posts’] in the controller becomes simply $posts in the view.

Now, let breakdown the create controller:

public function create()
{	
	if($_POST)
	{
		// Build post object
		$post = new Post_model();
		$post->title = $this->input->post('title', TRUE);
		$post->content = $this->input->post('content', TRUE);
		
		// Save post to database
		if ($post->save()) {
			redirect(base_url());
		}
	}

	// Load helpers
	$this->load->helper('form');

	// Initialize form
	$data['action'] = site_url('blog/create');
	$data['title'] = NULL;
	$data['content'] = NULL;
	
	// Load views	
	$this->load->view('header');
	$this->load->view('upsert', $data);
	$this->load->view('footer');
}
  • If the page request is a postback, we build and populate a new post object with the values stored in $_POST. Notice that we’re using CodeIgniter’s input class to retrieve $_POST values which provides two benefits. First, we don’t have to check if the $_POST value exists before trying to retrieve it. Second, setting the second parameter to true, lets us run the retrieved data through CodeIgniter’s XSS filter.
  • Once we’ve retrieved the values from $_POST we call the object’s save() method to insert the new values into the database.
  • If the insert operation is successful, we redirect the user to the List page using the redirect() and base_url() functions.
  • If the page request is not a postback, we load the form helper, initialize the form’s action and values, and load the form view.
  • Notice that we’re using the site_url() function to build the form action. This will prefix the segments passed to the function with the base_url and index_page we configured earlier..

Here’s the source code for the entire Blog controller class:

class Blog extends Controller
{
	public function __construct()
	{
		parent::Controller();
		
		// Load libraries
		$this->load->database();

		// Load helpers
		$this->load->helper('url');
			
		// Load models
		$this->load->model('Post_model', 'post');
	}
	
	public function index()
	{	
		// Get data from model
		$data['posts'] = $this->post->getAll();
		
		// Load views
		$this->load->view('header');
		$this->load->view('index', $data);
		$this->load->view('footer');
	}

	public function read()
	{
		// Get id from uri
		$id = $this->uri->segment(3);
		
		// Get data from model
		$data['post'] = $this->post->getById($id);
		
		// Load views
		$this->load->view('header');
		$this->load->view('read', $data);
		$this->load->view('footer');
	}
	
	public function create()
	{	
		if($_POST)
		{
			// Build post object
			$post = new Post_model();
			$post->title = $this->input->post('title', TRUE);
			$post->content = $this->input->post('content', TRUE);
			
			// Save post to database
			if ($post->save()) {
				redirect(base_url(), 'location');
			}
		}
	
		// Load helpers
		$this->load->helper('form');
	
		// Initialize form
		$data['action'] = site_url('blog/create');
		$data['title'] = NULL;
		$data['content'] = NULL;
		
		// Load views	
		$this->load->view('header');
		$this->load->view('upsert', $data);
		$this->load->view('footer');
	}
		
	public function update()
	{
		if ($_POST) 
		{
			// Build post object
			$post = new Post_model();
			$post->id = $this->uri->segment(3);
			$post->title = $this->input->post('title', TRUE);
			$post->content = $this->input->post('content', TRUE);
			
			// Save post to database
			if ($post->save()) {
				redirect(base_url(), 'location');
			}
		}
		
		// Get post from database
		$id = $this->uri->segment(3);
		$post = $this->post->getById($id);
		
		// Initialize form
		$this->load->helper('form');
		$data['action'] = site_url('blog/update/'.$id);
		$data['title'] = $post->title;
		$data['content'] = $post->content;

		// Load views	
		$this->load->view('header');
		$this->load->view('upsert', $data);
		$this->load->view('footer');
	}
	
	public function delete()
	{
		$post = new Post_model();
		$post->id = $this->uri->segment(3);
		if ($post->delete()) {
			redirect(base_url(), 'location');
		}
	}	
}

Before we wrap up this example, let’s take a look at all of the code that makes up the List operation:

Model

public function getAll()
{
	$query = $this->db->get('post');
	return $query->result();
}

View

<?php foreach($posts as $post) : ?>
	
	<h4>
		<?php echo anchor("blog/read/{$post->id}", $post->title); ?>
	</h4>
	
	<p>
		<?php echo $post->content; ?>
		<?php echo $post->created; ?>
	</p>
	
<?php endforeach; ?>

Controller

public function index()
{	
	// Get data from model
	$data['posts'] = $this->post->getAll();
	
	// Load views
	$this->load->view('header');
	$this->load->view('index', $data);
	$this->load->view('footer');
}

Conclusion

There’s a lot to like about CodeIgniter. The User Guide is an excellent resource full of useful examples. Compared to other frameworks, CodeIgniter’s installation is very simple. More importantly, I found CodeIgniter’s syntax to be self-documenting. For example, $this->load->database() loads the database library. Or, $this->post->getById() gets a blog post filtered by id. Or, $this->load->view(‘index’) loads the index view. Simple, right?

Next

A performance benchmark of the 8 Simple Blogs we developed.
Simple Blog – Performance Benchmark

Download

You can download the source code for each example at box.net.
Download Source

Advertisements

3 thoughts on “Simple Blog – Example 8: CodeIgniter

  1. Would appreciate if this post was updated for the latest Code Igniter. Spent a good deal of time resolving bugs and errors in the above code examples.

    Outside of that great tutorial and thank you for posting this.

Leave a Reply

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

WordPress.com Logo

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

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s