Published by Mijingo

movie icon image

ExpressionEngine How-to Articles

Topics range from beginner to advanced, but are all born out of real world, professional, day-to-day use of ExpressionEngine. Need more information? Get training videos and ebooks on ExpressionEngine from Mijingo.

Building with CodeIgniter: Adding the Blog

Editors Note: I’d like to welcome back Kenny Meyers as a guest author on EE Insider. Kenny is a web developer at Seattle, WA based Blue Flavor. He loves ExpressionEngine & CodeIgniter for their “less is more” approach and the ability to generate good, clean standards-compliant markup. He will be writing a series of articles on building a simple web application with CodeIgniter.

There is nothing in the world everyone wishes to hear more than your opinion. The primary vehicle for this is your blog. In the last article we began the process of building a tumblelog with CodeIgniter. In this, Part 2 of a four-part series, we will be building the blog in CodeIgniter. For brevity’s sake, many of the features that are standard on a blog have been cut-out. These include

  • Comments
  • Pagination
  • Admin Authentication

If you want help building or learning about these kind of features, I highly recommend the CodeIgniter forums.

So great it deserved a SQL

To begin writing blog posts, we must first create a database table to store the information. To build out the MySQL table, its important to understand the components of a blog post. For our use we need:

  • An entry
  • A summary
  • A title
  • A url title
  • A date of entry
  • An author field
  • And an entry id

To simplify this article, here is the SQL to build your blog table, with some built-in entry data.

CREATE TABLE blog (
    
entry_id integer(10PRIMARY KEY NOT NULL AUTO_INCREMENT,
    
summary TEXT NULL
    
entry TEXT NULL,
    
title varchar(255NOT NULL,
    
url_title varchar(200NOT NULL,
    
unique(url_title), 
    
creation_date DATETIME NOT NULL,
    
edited_date DATETIME NULL,
    
author VARCHAR(100)
);
    
INSERT INTO blog (`title`, `url_title`, `summary`, `entry`, `author`, `creation_date`) VALUES
('I love my mother''this_is_the_greates_thing_ever'
'My children, latest born to Cadmus old, Why sit ye here as suppliants, in your hands Branches of olive filleted with
wool? What means this reek of incense everywhere, And everywhere laments and litanies? '

'<p>OEDIPUS My children, latest born to Cadmus old, Why sit ye here as suppliants, in your hands Branches of olive filleted with wool? What means this reek of incense everywhere, And everywhere laments and litanies? Children, it were not meet that I should learn From others, and am hither come, myself, I Oedipus, your world-renowned king. Ho! aged sire, whose venerable locks Proclaim thee spokesman of this company, Explain your mood and purport. Is it dread Of ill that moves you or a boon ye crave? My zeal in your behalf ye cannot doubt; Ruthless indeed were I and obdurate If such petitioners as you I spurned.</p><p>PRIEST Yea, Oedipus, my sovereign lord and king, Thou seest how both extremes of age besiege Thy palace altars--fledglings hardly winged, And greybeards bowed with years, priests, as am I Of Zeus, and these the flower of our youth. Meanwhile, the common folk, with wreathed boughs Crowd our two market-places, or before Both shrines of Pallas congregate, or where Ismenus gives his oracles by fire. For, as thou seest thyself, our ship of State, Sore buffeted, can no more lift her head, Foundered beneath a weltering surge of blood. A blight is on our harvest in the ear, A blight upon the grazing flocks and herds, A blight on wives in travail; and withal Armed with his blazing torch the God of Plague Hath swooped upon our city emptying The house of Cadmus, and the murky realm Of Pluto is full fed with groans and tears.</p><p>Therefore, O King, here at thy hearth we sit, I and these children; not as deeming thee A new divinity, but the first of men; First in the common accidents of life, And first in visitations of the Gods. Art thou not he who coming to the town Of Cadmus freed us from the tax we paid To the fell songstress? Nor hadst thou received Prompting from us or been by others schooled; No, by a god inspired (so all men deem, And testify) didst thou renew our life. And now, O Oedipus, our peerless king, All we thy votaries beseech thee, find Some succor, whether by a voice from heaven Whispered, or haply known by human wit. Tried counselors, methinks, are aptest found To furnish for the future pregnant rede. Upraise, O chief of men, upraise our State! Look to thy laurels! for thy zeal of yore Our country\'s savior thou art justly hailedO never may we thus record thy reign:-- "He raised us up only to cast us down." Uplift usbuild our city on a rockThy happy star ascendant brought us luckO let it not decline! If thou wouldst rule This land, as now thou reignestbetter sure To rule a peopled than a desert realmNor battlements nor galleys aught avail, If men to man and guards to guard them tail.</p>', "Some Other Dude", NOW()),
('
This is the worst thing ever', 'the_worst_thing_ever', 'I MEAN to inquire if, in the civil orderthere can be any sure and legitimate rule of administrationmen being taken as they are and laws as they might beIn this inquiry I shall endeavour always to unite what right sanctions with what is prescribed by interestin order that justice and utility may in no case be divided.', '<p>I enter upon my task without proving the importance of the subjectI shall be asked if I am a prince or a legislatorto write on politicsI answer that I am neither, and that is why I do so. If I were a prince or a legislatorI should not waste time in saying what wants doingI should do it, or hold my peace.</p><p>As I was born a citizen of a free State, and a member of the SovereignI feel thathowever feeble the influence my voice can have on public affairsthe right of voting on them makes it my duty to study them: and I am happywhen I reflect upon governmentsto find my inquiries always furnish me with new reasons for loving that of my own country.</p><h2>1. SUBJECT OF THE FIRST BOOK</h2><p>MAN is born free; and everywhere he is in chainsOne thinks himself the master of others, and still remains a greater slave than theyHow did this change come aboutdo not knowWhat can make it legitimateThat question I think I can answer.</p><p>If I took into account only force, and the effects derived from itI should say"As long as a people is compelled to obey, and obeys, it does well; as soon as it can shake off the yoke, and shakes it off, it does still better; for, regaining its liberty by the same right as took it away, either it is justified in resuming it, or there was no justification for those who took it away." But the social order is a sacred right which is the basis of all other rightsNeverthelessthis right does not come from nature, and must therefore be founded on conventionsBefore coming to thatI have to prove what I have just asserted.</p>', 'Some Other Dude', NOW()); 

To run this SQL code, use the import tool on phpMyAdmin or your MySQL admin tool. To use the command line, please refer to the instructions on this page.

If you don’t understand SQL, or this looks like a strange dialect of Klingon, you can always learn at the MySQL docs.

More setup

Now that our database is setup, we need a model to grab information from the database. We want to build an entries and a listing page. First, we need to make sure that the database library is loaded, and configure CodeIgniter to do so. Open /tumbleupon/config/autoload.php and on line 42 set:

$autoload['libraries'= array('database'); 

The autoload parameter tells CodeIgniter which libraries to load on initialization. This means that you can omit the load function for a particular library. This is an extremely useful configuration variable, but use it wisely; load only that which you need.

Finally to make sure that our redirects and urls work correctly, open /tumbleupon/config/config.php and on line 14 set the base_url to whatever your host is. If you’re working locally this will likely be:

$config['base_url']    "http://localhost/"

With our configuration complete and database class loaded, it’s time to create our model.

It’s only a model

To build our model we need to consider what is to be extracted from the database. There are two pages that we are building: an entry listing page and an entry page. This means we’re going to require the Model to grab an entry or to grab all entries when a user requests a particular URL.

Note: It’s always best to consider keeping things DRY (Don’t Repeat Yourself) so where as a controller maps to URLs, a model needs to consider what a controller may need. A model has no ties to a URL. For our sake, since we’ll have listing and a single entry functionality that appear reusable, we can create a very simple model class:

<?php
class Blog_model extends Model {

    
public function __construct()
    
{
        parent
::__construct();
    
}
    
    
//Gets all entries in the blog table
    
public function get_all_entries()
    
{
        $this
->db->select('summary, title, url_title, author'); 
        
$query $this->db->get('blog');
        
        if (
$query->num_rows() > 0)
        
{
          
return $query->result();
        
else {
          
return array();
        
}

    }
    
    
//gets a single entry based on its url title
    
public function get_entry($url_title)
    
{
      $this
->db->select('title, entry, author')->where('url_title'$url_title);
      
$query $this->db->get('blog'1);
      
      if (
$query->num_rows() == 1)
      
{
        
return $query->row();
      
else {
        
return false
      
}
    }
}

/* End of file blog_model.php */
/* Location: ./tumbleupon/models/blog_model.php */ 
?> 

First, we need to create a Model that extends the Model class. We’re going to name it Blog_model. Save the file in /tumbleupon/models/ as blog_model.php.

Here is how this class works:

__construct() 

This function builds the Blog_model and extends the Model class.

get_all_entries() 
  • get_all_entries() grabs every single entry in the database table.
  • $this->db->select(‘summary, title, url_title, author’); is a mysql select statement wrapped in a CodeIgniter function from the database class. CodeIgniter will combine parameters until you call it with the $this->db->get(). This is best visible in the get_entry function. We select only what we need, in this case summary, title, url_title and author, so the query will be quicker.
  • We then run the db->get(‘blog’) which requests information from the database based on the query you’ve built (e.g. our previous select statement). The result is saved into a $query object.
  • We then run an if/else conditional to verify that data was returned. If no table rows are returned we return an empty array. If at least one 1 row is returned, we return the result object.
get_entry($url_title
  • get_entry asks for a $url_title handed to it from the controller.
  • It then builds a select statement for the specific columns it needs.
  • We use a syntax only available in PHP5 called chaining. This means instead of doing multiple $this->db->select, $this->db->where statements to generate our query we can just chain them together in an elegant form. We’re looking for entries where the url_title column equals our $url_title parameter.
  • The model runs the query and look for 1 row returned. If 1 is, we return a row object and if not we just return FALSE. This, and the empty array potentially returned in get_all_entries, will help us do error checking on the controller.

Now that we’ve built our model right and proper, we need to access it from the controller.

Flight controller

Now that we can grab data from the database, we need to build a blog controller to assist in displaying that data to the user. First, we create a controller file in /tumbleupon/controllers/ named blog.php. Since we have two types of pages, an entry and an entry listing page we only need two controller functions to handle it.

<?php
class Blog extends Controller {

  
public function __construct()
  
{
    parent
::__construct();
  
}

  
//the home page of the blog
  
public function index()
  
{
    $this
->load->model('Blog_model''blog');
    
$entries['query'$this->blog->get_all_entries();
    
$this->load->view('blog/index'$entries);
  
}

  
//For an individual entry, the url title is used to grab 
  //the entry
  
public function entry($url_title "")
  
{
      $this
->load->helper('url');
      
      if(
$url_title){
        $this
->load->model('Blog_model''blog');
        
$entry_data['post'$this->blog->get_entry($url_title);
        
        if(!
$entry_data['post']){
          redirect
('/blog''location');
        
else {
          $this
->load->view('blog/entry'$entry_data);
        
}
     } 
else {
       redirect
('/blog''location');
     
}
  }
}

/* End of file blog.php */
/* Location: ./system/tumbleupon/controllers/blog.php */ 

Let’s break the code down into chunks and go into more detail.

public function index()
{
  $this
->load->model('Blog_model''blog');
  
$entries['query'$this->blog->get_all_entries();
  
$this->load->view('blog/index'$entries);

The most important piece of information you must know about the $this->load function is that you use it almost everywhere, and it’s what grabs libraries, data, models etc. It loads what you tell it to and you can use what it loads.

The index function is loaded whenever someone loads the blog controller by hitting the /blog/ URL. In our case this is our entries view.

Our first action is to load the model. There are two parameters to this load->model function. The first is the model name and the second is how you would like to refer to the model. It is optional, but does make the code look cleaner. The next thing we do is create an array called entries that stores the results object returned from the Model in ‘queries’.

Finally, we load the view serving it the $entries array.

public function entry($url_title "")
{
    $this
->load->helper('url');
    
    if(
$url_title){
      $this
->load->model('Blog_model''blog');
      
$entry_data['post'$this->blog->get_entry($url_title);
      
      if(!
$entry_data['post']){
        redirect
('/blog''location');
      
else {
        $this
->load->view('blog/entry'$entry_data);
      
}
   } 
else {
     redirect
('/blog''location');
   
}
  • We setup the parameter for the entry function to have a default of “”
  • The parameter $url_title comes from the third segment of the url used to request the entry function in the blog controller. For example, blog/entry/this_entry would set the $url_title variable to this_entry.
  • We load the URL helper function, which is a set of functions for handling URLs, in order to use its redirect() function.
  • We test to see if there is a url_title, if not, we use the redirect function from the helper file to shoot the user back to the blog home page
  • If there is a $url_title we then load the model as we did in the index function and store the resulting object from our get_entry model function in an array called $entry_data.

In each of these functions, you will notice it loads a view, so it’s time that we built them.

La Vista Bonita

Views are relatively simple HTML documents. They are HTML templates that then output the data you give them from the controller. You “view” the data. So since we have two pages, we need two views.

I create a folder per controller for the views. This keeps the view directory clean and navigable for front-end developers. I’ve created a folder called blog in /tumbleupon/views/ and created two files, index.php and entry.php
. These files correlate to their controller functions which I think is a good practice to keep.

Here is the code for the index.php view:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
>
<
html>
<
head>
  <
meta http-equiv="Content-type" content="text/html; charset=utf-8">
  <
title>Blog</title>
  
</
head>
<
body id="blog_entry" onload="">
  <
h2>Blog</h2>
  
<?php foreach ($query as $entry{ ?>
  
<div class="blog_entry">
    <
h2><a href="/blog/entry/<?php echo $entry->url_title; ?>"><?php echo $entry->title?></a></h2>
    <
span class="metadata"><?php echo $entry->author?></span>
    <
p><?php echo $entry->summary?></p>
  </
div>
  
<?php } ?>
</body>
<
html

It’s a standard HTML page except we run a forloop over each of the $query objects that we passed with the controller. It outputs the data based on our column titles from the database table.

And here is the code for the entry.php view:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
>
<
html>
<
head>
  <
meta http-equiv="Content-type" content="text/html; charset=utf-8">
  <
title><?php echo $post->title?> Blog</title>
</
head>
<
body id="blog_entry" onload="">
  <
h2><?php echo $post->title?></h2>
  <
span class="metadata"><?php echo $post->author?></span>
  
<?php echo $post->entry?>
  
  
<p><a href="/index.php/blog/">Back to Blog</a></p>
  </
body>
<
html

The entry.php view is a much simpler: We are only loading one entry; we do not need to do a foreach loop; and can output the data directly to the templates.

With this new model, new view and controller we now have a functional blog.

Next time…

You may have noticed that we have no way to enter or edit posts. This is something we must fix, but we will be fixing it in the 4th part, administration. Our next episode deals with getting delicious and twitter posts into the mix and creating the life-stream you’ve always dreamed of.

Special Mention

Of special note, as I was finishing up this article, one of CodeIgniter’s more prolific community members Elliot Haughin has launched BinaryCake, a CodeIgniter and PHP screencast website. I downloaded the first version of his screencast and watched as Elliot built a blog and setup CodeIgniter! This, of course, fits perfectly with what we’re discussing here but he goes more in-depth with his code and explanation. I highly suggest you check out the screencasts and learn even more about CodeIgniter.

Posted on Jun 17, 2009

Filed Under: How-To, CodeIgniter, Web Development,

Kenny Meyers
About Kenny Meyers

Kenny Meyers is a web developer based out of San Francisco who currently works for the excellent Virb.com. Kenny has worked on the web in some capacity for 12 years and has helped organize, write and build some great ExpressionEngine sites including Mithun.com and the upcoming Omni Group website. When he's not crying alone, Kenny writes regularly, abusing adjectives and hyperbole, at Happy Magic Fun Time and runs his mouth on twitter @kennymeyers.