CodeIgniter – Extending CI_Model

Today I would like to discuss about CodeIgniter (CI) models and how you can achieve code reuse by “overriding” the base CI_Model class.

First let’s take a simple example where you have two models in your application : employee, employee_leave

You would normally create two classes :
[php]
class Employee extends CI_Model {

public function get_by_id($id)
{
//some repository operation
}

}

class Employee_Leave extends CI_Model {

public function get_by_employee_id($id)
{
//some repository operation
}

}
[/php]

which is fine, and in your controller if you need to get an employee by id you would do something like :
[php]
$this->employee->get_by_id(/* id of your employee*/);
[/php]

same for your employee leave
[php]
$this->employee_leave->get_by_employee_id(/* id of your employee*/);
[/php]

The implementation of get_by_id and get_by_employee_id would be a SELECT query to the underlying data source. Using Active Record class, it would be  :
[php]
$query = $this->db->get_where(’employee’, array(‘id’ => $id), $limit, $offset);
[/php]
and
[php]
$query = $this->db->get_where(’employee_leave’, array(’employee_id’ => $employee_id), $limit, $offset);
[/php]
A more elegant way to work out the same operation would be by overriding the base CI_Model and adding generic functions.
[php]

class MY_Model extends CI_Model {

// will hold the table name of the current instance
var $tablename = “”;

// this constructor will help us initialize our child classes
public function __construct($tablename)
{
$this->tablename = $tablename;
parent::__construct();
}

public function get_all($limit = -1, $offset = 0, $orderby = ”) {}

public function get_total_count() {}

public function get_total_count_where($where) {}

public function get_where($where = array(), $limit = 10, $offset = 0, $orderby = ”) {}

}
[/php]

By building building this custom Model with an overloaded constructor that will take the table name as parameter, you will be simplifying the child classes build on top.

You need to modify your current Model (employee and employee_leave) to make use of your new base Model.

[php]
class Employee extends MY_Model {

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

}

class Employee_Leave extends MY_Model {

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

}
[/php]

 

In you controller the code will now be :

[php]
$this->employee->get_where(/* where condition with id of your employee*/);
[/php]

same for your employee leave
[php]
$this->employee_leave->get_where(/* where condition with id of your employee*/);
[/php]

 

A very basic example that can be extended to build some solid and complex logic around your Model so that you don’t repeat the same code for performing repository operation.

CodeIgniter – Pagination SEO Issue

I have recently been working with a PHP MVC Framework called CodeIgniter on a complete web application solution.  I have been trying some major framework like CakePHP, Zen and Symphony which where all very powerful framework for MVC and RAD development, the only thing they lack was a bit more of flexibility like CodeIgniter propose. Anyway may not have taken enought time to get to know all of the specifics of the other Frameworks, but while benchmarking i got aquainted to CodeIgniter much faster.

Even though CodeIgniter is a very flexible framework, it’s very lightweight and some feature for Web application have not been taken into account, that in mind, the people behind EllisLab, Inc made sure that these small twigs were easily bypassed by allowing complete customization of their libraries.

Here is my original issue:

I have a item listing page with pagination activated and I wanted the first page to be the the root URL of the item page.
e.g. http://www.mysite.com/items

But what CodeIgniter Pagination Library generated for the first page was: http://www.mysite.com/result/1

That is pretty inconvenient for SEO, because the crawler will find two pages with the same content while crawling the pages.

Thus i modified the CI_Pagination library an created MY_Pagination.

First of all i have added a new variable called first_page_url as class variable in MY_Pagination class

[php]

class MY_Pagination extends CI_Pagination {

var $first_page_url        = ”; // The first page will have this URL

[/php]

I have changed the original Pagination Library First page rendering from

[php]

// Render the “First” link
if  ($this->cur_page > ($this->num_links + 1))
{
$output .= $this->first_tag_open.'<a href=”‘.$this->base_url.'”>’.$this->first_link.'</a>’.$this->first_tag_close;
}

[/php]

to

[php]

// Render the “First” link
if  ($this->cur_page > ($this->num_links + 1))
{
$output .= $this->first_tag_open.'<a href=”‘.$this->first_page_url == ” ? $this->base_url : $this->first_page_url.'”>’.$this->first_link.'</a>’.$this->first_tag_close;
}

[/php]

This way if during the initialization of the Pagination class the configuration setting first_page_url was passed it will be used instead of the base_url.

Some modification were also made to the pagination digit generation from

[php]

// Write the digit links
for ($loop = $start -1; $loop <= $end; $loop++)
{
$i = ($loop * $this->per_page) – $this->per_page;

if ($i >= 0)
{
if ($this->cur_page == $loop)
{
$output .= $this->cur_tag_open.$loop.$this->cur_tag_close; // Current page
}
else
{
$n = ($i == 0) ? ” : $i;
$output .= $this->num_tag_open.'<a href=”‘.$this->base_url.$n.'”>’.$loop.'</a>’.$this->num_tag_close;
}
}
}

[/php]

to

[php]

// Write the digit links
for ($loop = $start -1; $loop <= $end; $loop++)
{
$i = ($loop * $this->per_page) – $this->per_page;

if ($i >= 0)
{
if ($this->cur_page == $loop)
{
$output .= $this->cur_tag_open.$loop.$this->cur_tag_close; // Current page
}
else if($loop == 1 && $this->first_page_url != ”)
{
$output .= $this->num_tag_open.'<a href=”‘.$this->first_page_url.'”>’.$loop.'</a>’.$this->num_tag_close;
}
else
{
$n = ($i == 0) ? ” : $i;
$output .= $this->num_tag_open.'<a href=”‘.$this->base_url.$n.'”>’.$loop.'</a>’.$this->num_tag_close;
}
}
}

[/php]

which will make sure that the page numbered 1 takes has the first_page_url has href when  first_page_url is available.

The complete file can be found here: MY_Pagination