CodeIgniter Model

 

Introduction

In the previous tutorial, we created database migrations files and also seeded some dummy data into our database. This tutorial builds on the previous one and introduces you to models in CodeIgniter. If you have not yet read the previous tutorial then I recommend you read it.
By the end of this tutorial, you will know who to work with models in CodeIgniter like a pro. You will also learn about best practices will make you write cleaner and concise code.
CodeIgnter Admin Panel

Topics to be covered

We will cover the following topics in this tutorial
  • Tutorial pre-requisites
  • Skinny model skinny controller
  • Model conventions
  • CodeIgniter Model Example
  • Extending CodeIgniter Model
  • CodeIgniter Model Generator
  • Load model in controller
  • CodeIgniter Admin Panel Tutorial Project models

Tutorial Pre-requisites

This tutorial assumes you are familiar with;
  • PHP basics and Object-oriented programming
  • CodeIgniter Basics
  • You have a web server, PHP and MySQL already configured and running
  • You have a cool IDE i.e. NetBeans IDE, Aptana Studio etc.
  • You have access to a command line/terminal that you can use to run commands
  • You have composer. Composer is a PHP dependencies manager.

Skinny model skinny controller

The common myth when working with MVC frameworks is to make your models skinny and your controllers skinny. This makes models God Objects and this is an anti-pattern. The rule of divide and conquer tells us all classes should be skinny.
We will create a model base class that will extend CodeIgniter model and put all common functionality in the model base class. This will help us to write DRY (Don’t Repeat Yourself) code and make our models thin

Model conventions

One of the things that ruby on rails developers love about the framework is conventions over configurations. This means the framework has default conventions that it automatically applies and assumes you are following them. Other PHP frameworks such as Laravel have also embraced this concept.
This approach has two main advantages
  1. You write less code which leads to rapid application development
  2. Sets industry standards that everyone follows.
We will also adopt some of these conventions. The following lists the conventions that we will be using
  1. Model name should be singular while table name should be plural. If you have a model Cat, then the corresponding database table name should be cats. Since this is an agreed upon convention, you will not need to set the table name for your model. The framework will simply pluralize the model name. If you want to use a table name that cannot be pluralized, then you can specify the table name
  2. Each database table should have an auto increment primary key called id – all database interactions will assume id is the primary key and the framework that take care of that for you.
  3. Each database table will contain date created and date updated fields – this is for auditing purposes. Our tutorial series will use datecreated and dateupdated. The common convention that rails and Laravel use are createdat and updatedat. In my opinion, datecreated is more description than createdat. Feel free to go with whatever works for you.
  4. Each database table will contain createdfromip and updatedfromip fields – this is also for auditing purposes. The IP fields are not included in Rails and Laravel by default.

CodeIgniter Model Example

The following code shows an example of a typical model in CodeIgniter.

<?php

class Blog_model extends CI_Model {

    public $title;
    public $content;
    public $date;

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

    public function get($id){
        return $this->db->get_where('posts', array('id' => $id))->row();
    }

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

    public function insert() {
        $this->title = 'CodeIgniter 101';
        $this->content = '<p>Say what you want about CI, it still rocks</p>';
        $this->date = time();

        $this->db->insert('posts', $this);
    }

    public function update($id) {
        $this->title = 'CodeIgniter 101';
        $this->content = '<p>Say what you want about CI, it still rocks</p>';
        $this->date = time();

        $this->db->update('posts', $this, array('id' => $id));
    }

    public function delete($id){
        $this->db->delete('posts', array('id' => $id)); 
    }
}
HERE,
  • class Blog extends CI_Model {} defines a class Blog that extends CI_Model
  • public function __construct() {} defines the class constructor method that executes the parent class constructor method
  • public function get($id){} returns a single record based on the supplied primary key value
  • public function get_all() {} returns all records
  • public function insert() {} adds a new record to the table
  • public function update($id) {} updates an existing record based on the supplied id
  • public function delete($id){} deletes an existing record based on the supplied id

Extending CodeIgniter Model

We don’t just want skinny controllers, we want skinny models too. We will create a base class that will have the following six (6) functions. We will apply the agreed upon conventions in this section.
  1. get($id) returns single record based on the supplied id
  2. get($fields = '', $where = array()) returns records based on specified field names and criteria
  3. get_all() returns all the records
  4. insert() creates a new record
  5. update($id) updates an existing record
  6. delete($id) deletes an existing record
For CodeIgniter to auto load our base model, we will need to save it in /application/core as MY_Model.php
Create a new file /application/core/MY_Model.php
Add the following code

<?php (defined('BASEPATH')) OR exit('No direct script access allowed');

class MY_Model extends CI_Model {

    protected $table_name = '';
    protected $primary_key = 'id';

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

        $this->load->database();

        $this->load->helper('inflector');

        if (!$this->table_name) {
            $this->table_name = strtolower(plural(get_class($this)));
        }
    }

    public function get($id) {
        return $this->db->get_where($this->table_name, array($this->primary_key => $id))->row();
    }

    public function get_all($fields = '', $where = array(), $table = '', $limit = '', $order_by = '', $group_by = '') {
        $data = array();
        if ($fields != '') {
            $this->db->select($fields);
        }

        if (count($where)) {
            $this->db->where($where);
        }

        if ($table != '') {
            $this->table_name = $table;
        }

        if ($limit != '') {
            $this->db->limit($limit);
        }

        if ($order_by != '') {
            $this->db->order_by($order_by);
        }

        if ($group_by != '') {
            $this->db->group_by($group_by);
        }

        $Q = $this->db->get($this->table_name);

        if ($Q->num_rows() > 0) {
            foreach ($Q->result_array() as $row) {
                $data[] = $row;
            }
        }
        $Q->free_result();

        return $data;
    }

    public function insert($data) {
        $data['date_created'] = $data['date_updated'] = date('Y-m-d H:i:s');
        $data['created_from_ip'] = $data['updated_from_ip'] = $this->input->ip_address();

        $success = $this->db->insert($this->table_name, $data);
        if ($success) {
            return $this->db->insert_id();
        } else {
            return FALSE;
        }
    }

    public function update($data, $id) {
        $data['date_updated'] = date('Y-m-d H:i:s');
        $data['updated_from_ip'] = $this->input->ip_address();

        $this->db->where($this->primary_key, $id);
        return $this->db->update($this->table_name, $data);
    }

    public function delete($id) {
        $this->db->where($this->primary_key, $id);

        return $this->db->delete($this->table_name);
    }
}
HERE,
  • protected $table_name = ''; defines class property for the table name and sets it to blank by default
  • protected $primary_key = 'id'; defines the class property for the primary key and sets it to id by default. This is a convention that we agreed upon
  • public function __construct() {} is the class constructor method. parent::__construct(); executes the parent class constructor method. $this->load->database(); loads the database class. $this->load->helper('inflector'); loads the inflector helper. The inflector helper will help us pluralize the class name to guess the database table name.
  • if (!$this->table_name) {} check if the class name is not set explicitly. If it is not set then we pluralize the class name and set it has the table name. If it is set, we go with the set table name
  • The rest of the code is self-explanatory. Feel free to use the comments section to ask for clarifications.

CodeIgniter Model Generator

One of the things that I love about programming is I AM ALLOWED TO BE LAZY. Why waste time writing boiler plate code when I can automate the process? In the previous tutorial, we created a command line tool that helped us generate models. We will add one more function to generating models boiler plate code. Open /application/controllers/tools.php
Add the following functions

public function model($name) {
    $this->make_model_file($name);
}

protected function make_model_file($name) {
    $path = APPPATH . "modules/admin/models/$name.php";

    $my_model = fopen($path, "w") or die("Unable to create model file!");

    $model_template = "<?php (defined('BASEPATH')) OR exit('No direct script access allowed');

    class $name extends MY_Model {

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

    fwrite($my_model, $model_template);

    fclose($my_model);

    echo "$path model has successfully been created." . PHP_EOL;
}
Now that we have our command line tool in place, let’s auto generator our first model
Open the command prompt / terminal
Run the following command to browser to the project root. Note: the project root should point to wherever you created the project

cd "C:\xampp\htdocs\ci-my-admin"
Run the following command to generate the model for brands

php index.php tools model "Brand"
Note: the model name is singular. This is in accordance with conventions over configurations
You will get the following message

C:\xampp\htdocs\ci-my-admin\application\modules/admin/models/Brand.php model has successfully been created.
Open /application/modules/admin/models/brand.php
You will get the following

<?php (defined('BASEPATH')) OR exit('No direct script access allowed');

class Brand extends MY_Model {

    public function __construct() {
        parent::__construct();
    }
}
HERE,
  • class Brand extends MY_Model {} defines a class Brand that extends our base model MY_Model
  • public function __construct() {} defines the class constructor that executes the parent constructor
Just like that, our model is ready for use. Since we are taking advantage of conventions over configurations, we don’t have to write a single code. Our model is skinny too : ). The next section shows you how to use our new model. Keep calm and reading

Load model in controller

This section shows you how to load a model in CodeIgniter. In the previous tutorials, we created controllers for our brands, categories and products tables. Open /application/modules/admin/controllers/Brands.php
Modify the constructor method to the following

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

    $this->load->model(array('admin/brand'));
}
HERE, $data = $this->brand->get_all(); loads the brand model in the constructor method. This will make the model available to all methods in Brands controller.
Add the following method

public function brands_list(){
    $data = $this->brand->get_all();

    print_r($data);
}
HERE,
  • $data = $this->brand->get_all(); calls the get_all() method of our brand controller.
  • print_r($data); dumps the contents of $data in the browser
Open /application/config/routes.php
Add the following route for testing purposes

$route['brands'] = 'admin/brands/brands_list';
Load the following URL in your browser

http://localhost/ci-my-admin/brands
You will get results similar to the following
CodeIgniter Model
We got the above results from the database with almost zero (0) coding on our part.

CodeIgniter Admin Panel Tutorial Project models

We already generated the model for Brand. Let’s now generate the models for Category and Product
Run the following commands. Make sure you are in the root of your project

php index.php tools model "Category" 
php index.php tools model "Product"
That was it, we are done with creating the models.
Let’s now modify our controllers to pass the data to views
Brands.php

<?php

class Brands extends Admin_Controller {

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

        $this->load->model(array('admin/brand'));
    }

    public function index() {
        $brands = $this->brand->get_all();

        $data['brands'] = $brands;
        $data['page'] = $this->config->item('ci_my_admin_template_dir_admin') . "brands_list";
        $this->load->view($this->_container, $data);
    }

    public function create() {
        if ($this->input->post('description')) {
            $data['description'] = $this->input->post('description');
            $this->brand->insert($data);

            redirect('/admin/brands', 'refresh');
        }

        $data['page'] = $this->config->item('ci_my_admin_template_dir_admin') . "brands_create";
        $this->load->view($this->_container, $data);
    }

    public function edit($id) {
        if ($this->input->post('description')) {
            $data['description'] = $this->input->post('description');
            $this->brand->update($data, $id);

            redirect('/admin/brands', 'refresh');
        }

        $brand = $this->brand->get($id);

        $data['brand'] = $brand;
        $data['page'] = $this->config->item('ci_my_admin_template_dir_admin') . "brands_edit";
        $this->load->view($this->_container, $data);
    }

    public function delete($id){
        $this->brand->delete($id);

        redirect('/admin/brands', 'refresh');
    }
}
Categories.php

<?php

class Categories extends Admin_Controller {

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

        $this->load->model(array('admin/category'));
    }

    public function index() {
        $categories = $this->category->get_all();

        $data['categories'] = $categories;
        $data['page'] = $this->config->item('ci_my_admin_template_dir_admin') . "categories_list";
        $this->load->view($this->_container, $data);
    }

    public function create() {
        if ($this->input->post('description')) {
            $data['description'] = $this->input->post('description');
            $this->category->insert($data);

            redirect('/admin/categories', 'refresh');
        }

        $data['page'] = $this->config->item('ci_my_admin_template_dir_admin') . "categories_create";
        $this->load->view($this->_container, $data);
    }

    public function edit($id) {
        if ($this->input->post('description')) {
            $data['description'] = $this->input->post('description');
            $this->category->update($data, $id);

            redirect('/admin/categories', 'refresh');
        }

        $category = $this->category->get($id);

        $data['category'] = $category;
        $data['page'] = $this->config->item('ci_my_admin_template_dir_admin') . "categories_edit";
        $this->load->view($this->_container, $data);
    }

    public function delete($id) {
        $this->category->delete($id);

        redirect('/admin/categories', 'refresh');
    }
}
Products.php

<?php

class Products extends Admin_Controller {

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

        $this->load->model(array('admin/brand'));
        $this->load->model(array('admin/category'));
        $this->load->model(array('admin/product'));
    }

    public function index() {
        $products = $this->product->get_all();

        $data['products'] = $products;
        $data['page'] = $this->config->item('ci_my_admin_template_dir_admin') . "products_list";
        $this->load->view($this->_container, $data);
    }

    public function create() {
        if ($this->input->post('name')) {
            $data['name'] = $this->input->post('name');
            $data['price'] = $this->input->post('price');
            $data['model'] = $this->input->post('model');
            $data['brand_id'] = $this->input->post('brand_id');
            $data['category_id'] = $this->input->post('category_id');
            $data['tag_line'] = $this->input->post('tag_line');
            $data['features'] = $this->input->post('features');

            $this->product->insert($data);

            redirect('/admin/products', 'refresh');
        }

        $brands = $this->brand->get_all();
        $categories = $this->category->get_all();

        $data['brands'] = $brands;
        $data['categories'] = $categories;
        $data['page'] = $this->config->item('ci_my_admin_template_dir_admin') . "products_create";
        $this->load->view($this->_container, $data);
    }

    public function edit($id) {
        if ($this->input->post('name')) {
            $data['name'] = $this->input->post('name');
            $data['price'] = $this->input->post('price');
            $data['model'] = $this->input->post('model');
            $data['brand_id'] = $this->input->post('brand_id');
            $data['category_id'] = $this->input->post('category_id');
            $data['tag_line'] = $this->input->post('tag_line');
            $data['features'] = $this->input->post('features');
            $this->product->update($data, $id);

            redirect('/admin/products', 'refresh');
        }

        $product = $this->product->get($id);
        $brands = $this->brand->get_all();
        $categories = $this->category->get_all();

        $data['brands'] = $brands;
        $data['categories'] = $categories;
        $data['product'] = $product;
        $data['page'] = $this->config->item('ci_my_admin_template_dir_admin') . "products_edit";
        $this->load->view($this->_container, $data);
    }

    public function delete($id) {
        $this->product->delete($id);

        redirect('/admin/products', 'refresh');
    }
}

Displaying data from models in view

For the sake of brevity, we will not include the code for the views in this tutorial. You should download the attached tutorial files. The views have been updated to display data

Summary

In this tutorial, we have learnt how to create skinny models and take advantage of conventions over configurations. We also added common fields to our database for auditing purposes that the base model is taking care of.