CodeIgniter 101: Models

Models are the most important part of any database driven web application in CodeIgniter. Models allow us to keep our database code separated from our basic program flow control (Controllers) and our output (Views) which is a necessary discipline of writing clean and well structured code using CodeIgniter. Once you truly get used to the structured discipline and implementation of the Model-View-Controller (MVC) design pattern, you will be absolutely proud to see the difference between the quality and structure of your code. This article discusses the basics of Models in CodeIgniter and how to easily implement basic CRUD functions.

Because CodeIgniter (when properly configured) automatically handles the connections to your database, we can get straight to the construction of the model. Generally, you should have one model for each table in your database. Each model should at least contain a function to create, retrieve, update and delete (CRUD) records data into / from the table it is responsible for. This is not to say that the model should not contain more specialized functions, because it can and depending on the complexity of your application, it probably will.

As a working example, we will build a simple model that has the responsibility of managing a table which stores basic personal contact information. We won't worry about normalization during this example since we're working with only one table for simplicity's sake.

THE TABLE SCHEMA

CREATE TABLE `test`.`contacts` (
`contact_id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`last_name` VARCHAR( 25 ) NOT NULL ,
`first_name` VARCHAR( 25 ) NOT NULL ,
`phone` VARCHAR( 15 ) NOT NULL ,
`city` VARCHAR( 25 ) NOT NULL ,
`state` VARCHAR( 25 ) NOT NULL ,
`zip` VARCHAR( 10 ) NOT NULL
) ENGINE = MYISAM

As you can see, this table stores a contact's name, phone number and address information. What we will do is create a model with 3 functions: get, save and delete. The save function will be used to both insert and update a record depending on the state of the model when the save function is called.

THE MODEL

mdl_contacts.php

class Mdl_contacts extends Model {

    // We will give this model the following properties so they can be set dynamically from the controller
    var $contact_id;
    var $last_name;
    var $first_name;
    var $phone;
    var $street;
    var $city;
    var $state;
    var $zip;

    function Mdl_contacts() {

        // All models need to call the constructor of the parent Model class
        parent::Model();

    }

    function get() {

        // BEGIN FILTER CRITERIA CHECK
        // If any of the following properties are set before mdl_contacts->get() is called from the controller then we will include
        // a where statement for each of the properties that have been set.
        if ($this->contact_id) {
            $this->db->where("contact_id", $this->contact_id);
        }

        if ($this->last_name) {
            $this->db->where("last_name", $this->last_name);
        }

        if ($this->first_name) {
            $this->db->where("first_name", $this->first_name);
        }

        if ($this->phone) {
            $this->db->where("phone", $this->phone);
        }

        if ($this->street) {
            $this->db->where("street", $this->street);
        }

        if ($this->city) {
            $this->db->where("city", $this->city);
        }

        if ($this->state) {
            $this->db->where("state", $this->state);
        }

        if ($this->zip) {
            $this->db->where("zip", $this->zip);
        }
        // END FILTER CRITERIA CHECK

        // We will display our results in order by last name and then first name.
        $this->db->orderby("last_name, first_name");

        // This will execute the query and collect the results and other properties of the query into an object.
        $query = $this->db->get("contacts");

        // If you set mdl_contacts->contact_id from your controller, then there will only be one row to return.
        if ($this->contact_id) {
            return ($query->row());
        }

        // If mdl_contacts->contact_id was not specified in the controller, then we will return the results as a result set.
        else {
            return ($query->result());
        }

    }

    function save() {

        // When we insert or update a record in CodeIgniter, we pass the results as an array:
        $db_array = array(
        "last_name" => $this->last_name,
        "first_name" => $this->first_name,
        "phone" => $this->phone,
        "city" => $this->city,
        "state" => $this->state,
        "zip" => $this->zip);

        // If mdl_contacts->contact_id was set in the controller, then we will update an existing record.
        if ($this->contact_id) {
            $this->db->where("contact_id", $this->contact_id);
            $this->db->update("contacts", $db_array);
        }

        // If mdl_contacts->contact_id was not set in the controller, then we will insert a new record.
        else {
            $this->db->insert("contacts", $db_array);
        }

    }

    function delete() {

        // As long as mdl_contacts->contact_id was set in the controller, we will delete the record.
        if ($this->contact_id) {
            $this->db->where("contact_id", $this->contact_id);
            $this->db->delete("contacts");
        }

    }

}

HOW TO USE THE MODEL FROM THE CONTROLLER

Note: Because this is an introductory tutorial, we will not get into paginating results, but we will cover pagination in an upcoming tutorial.

Before we can use mdl_contacts from a controller, we need to load the model:

$this->load->model("mdl_contacts");

The best place to load the model is usually from the controller's constructor, especially if you plan to use the model from multiple functions within the controller.

Once we have the model loaded, we can start using its functions.

RETRIEVING DATA

Through our mdl_contacts->get() function, we can retrieve the table rows that the model is responsible for.

Retrieving All Rows
To retrieve all of the rows from the table, we would simply store the results in a variable by calling the get() function as such:

$results = $this->mdl_contacts->get(); // $results now contains the results of the query
// SQL Generated: SELECT * FROM contacts ORDER BY last_name, first_name

Retrieving Filtered Rows
To retrieve only one record by the primary key, we would first set the property of the model and then call the get() function:

$this->mdl_contacts->contact_id = 5;
$results = $this->mdl_contacts->get();
// SQL Generated: SELECT * FROM contacts WHERE contact_id = 5 ORDER BY last_name, first_name

This method is most useful when we want to retrieve a single record to populate a form for editing.

We might want to filter the results for anybody with the first name of Bob. To do so:

$this->mdl_contacts->first_name = "Bob";
$results = $this->mdl_contacts->get(); // Now $results will contain any database record that has a Bob as the first name.
// SQL Generated: SELECT * FROM contacts WHERE first_name = 'Bob' ORDER BY last_name, first_name

Or maybe we want to filter any Bobs who live in Houston:
$this->mdl_contacts->first_name = "Bob";
$this->mdl_contacts->city = "Houston";
$results = $this->mdl_contacts->get();
// SQL Generated: SELECT * FROM contacts WHERE first_name = 'Bob' AND city = 'Houston' ORDER BY last_name, first_name

Because of the way we set up the get() function in the model, if we set the object properties before we retrieve the records, our model will automatically construct a WHERE segment for the query and return the filtered results accordingly.

SAVING DATA

Saving data is as easy as passing values to the object and executing the save() function.

Saving a New Record
The following example will create and execute an INSERT statement since we are not passing a contact_id to the model:

$this->mdl_contacts->first_name = "Jesse";
$this->mdl_contacts->last_name = "Terry";
$this->mdl_contacts->phone = "123-456-7890";
$this->mdl_contacts->city = "Flagstaff";
$this->mdl_contacts->state = "AZ";
$this->mdl_contacts->zip = "86001";
$this->mdl_contacts->save();

Updating an Existing Record
The following example passes a contact_id to the model, so our model will create and execute an UPDATE statement:

$this->mdl_contacts->contact_id = 1;
$this->mdl_contacts->first_name = "Jesse";
$this->mdl_contacts->last_name = "Terry";
$this->mdl_contacts->phone = "123-456-7890";
$this->mdl_contacts->city = "Flagstaff";
$this->mdl_contacts->state = "AZ";
$this->mdl_contacts->zip = "86001";
$this->mdl_contacts->save();

Notice that the only difference between saving a new record and updating an existing record is that we pass contact_id to the model if we want to update it. This is because of the condition we built into the save() function in the model.

In a real-world application, you won't hard-code the model properties as "Jesse", "Terry", etc. You'll most likely be working with form data which would utilize $this->input->post("var_name") to achieve a dynamic state. Details on forms and input will be covered in a future tutorial.

Deleting a Record

$this->mdl_contacts->contact_id = 1;
$this->mdl_contacts->delete();

As you can see, a functional and simple model with CRUD methods is not difficult to achieve. On its own, a model doesn't do much good without controllers that dictate the flow of the application and views that let us know what is happening with the application. In the next tutorial, we will build a controller that works together with this model to see how the two fit together.

Posted in CodeIgniter by Jesse Terry on 12/30/2007 | 15 Comments

Comments:

Khoa on 08/09/2008 `at` 03:33 AM:

PS: finally!!!....got your captcha right...

Khoa on 08/09/2008 `at` 03:32 AM:

Very good article Jesse. I'm programming in CF using OO style, just wonder can OOP and MVC go together? Or they are just 2 separate ways of programming? And if they can go, how are they connected/related? Thanks Jesse, looking forward to the next article. PS: sorry, but is there something wrong with your captcha? I surely typed it correctly twice!!!..and now 3 times...giving up soon...4 times...

cheekygeek on 06/21/2008 `at` 07:50 PM:

You are good at explaining things in a clear and understandable way. I'd love to see your followup tutorial. I'm not clear on how the controller works together with the model, your promised next topic.

Jesse Terry on 06/09/2008 `at` 06:39 AM:

Thanks Daniel - nice contribution!

Daniel on 06/02/2008 `at` 02:07 PM:

Great :) But I'm think that you can modify get/insert method like this (for get() method): function get() { foreach(get_class_vars(get_class($this)) as $key=>$value) { if($key != "_parent_name") { $val = $this->$key; if($val) $this->db->where($key, $val); } } .... } :) in my way this is very useable.

elitemedia on 05/26/2008 `at` 03:20 PM:

Thanks for the reply. Could you reply to the question of Thomas below about joined tables? (I am also interested to find how I must to do because I started to use your technique some days ago)

Jesse Terry on 05/26/2008 `at` 04:01 AM:

Yeah I'm way behind on getting the next tutorial out... it'll be out soon though! :)

elitemedia on 05/24/2008 `at` 11:33 PM:

Hey Jesse, awesome tutorial, but where is the next article that you've promised?

Frank on 05/06/2008 `at` 12:24 AM:

Hey Jesse, Awesome article. I love how you wrap the add/edit into a single function... really nice! Any chance you'll be posting the follow-ups you mentioned in the article?

Thomas on 04/06/2008 `at` 10:26 AM:

Thanks for the great article. A question: How do you usually handle cases where you have joined tables? Do you execute the joins every time? i.e. if you have users working for clients you would have to join the tables to get the client whom a user works for, but if you only want to get users firstname, it would suffice to look at the user table only...? PS: agree with Cambo about your captcha - too hard

jeremy on 04/04/2008 `at` 11:14 PM:

just came across this post, and i'm really impressed. the ci models documentation has always been weak for someone who doesn't come from a MVC background. this really helps.

Jesse Terry on 02/26/2008 `at` 05:02 AM:

The follow-up will be here soon, thanks!

Cambo on 02/14/2008 `at` 10:55 AM:

PS. I hate your captcha. It is very hard to read. :)

Cambo on 02/14/2008 `at` 10:52 AM:

Thanks. Great article. I now understand something that I didn't. When is the follow up?

Michael on 01/05/2008 `at` 03:53 AM:

Terry; Do you have contact form or email address somewhere that one could get a hold you by? Thanks.


Search This Site


Categories

Blogroll

Users