Revisiting MY_Model in CodeIgniter: Copying from Jamie Rumbelow and looking at Eloquent

Created at: February 11, 2015; Last update: March 2, 2015
This tutorial turned into a Github repository.
Please give it a try at:
https://github.com/avenirer/CodeIgniter-MY_Model

This tutorial is a work in progress created at 2015.02.11 and updated at 2015.02.17. It’s tested with CodeIgniter 3.

Well… It’s that time when we should stop repeating ourselves. The DRY philosophy tells us that maybe we should use our time doing things faster than repeating the same steps for basic CRUD in our models. I’ve talked about the MY_Model subject in the past, but we can improve a lot on that.

A few years ago, Jamie Rumbelow wrote a great book named CodeIgniter Handbook in three volumes, in which he manages to bring Ruby philosophy in the world of CodeIgniter. In the book he talks about Convention over Configuration, trying to apply design principles like Don’t Repeat Yourself (DRY) and RESTful style to CodeIgniter.

He starts the book writing about his MY_Model, model which is freely accessible on Github (https://github.com/jamierumbelow/codeigniter-base-model). I would really advise you to try his MY_Model, and you won’t regret it.

This post is actually not about Jamie’s model, but about plagiarism, or, if you will, about learning how to make your own MY_Model by copying from the best.

Before we dive into the problem, let’s see the “rules of engagement”:

  • one model must reflect the one table
  • table names must be in plural
  • model names must be in singular with the “_model” at the end
  • each table should contain an id

Taking this into consideration, let’s start the project by creating a MY_Model inside the application/core directory.

We will start the file with a commented example of how to use the MY_Model, and then we will start copying Jamie Rumbelow’s model, creating our model having Jamie Rumbelow’s base model as source:

Now, you only have to extend the MY_Model in all your models.

Setting the database connection

There are times when we use different connections depending on what we need them for: for inserting, updating, or deleting we use a connection (which has writing rights), and for selecting we use another connection (which doesn’t have writing rights), so why not “injecting” the connection into our MY_Model.

If you look inside the application/config directory, you should see something in the lines of these:

Now, if you need to have two connections, you can simply add another $db key:

If you need to know more about how to connect to multiple databases in CodeIgniter you could try another of my tutorials.

Now, going into our MY_Model, we will use a private method to setup the connection to the database:

But what if, for some reason, we want to use a certain connection only for a method and not for the whole class? I’ve seen something similar in Eloquent where, if you want, you can use an on() method when doing a query. So let’s create one ourselves for MY_Model:

Now, when we want something from a model and want to use another connection than the default set for the whole model we can simply do something like this: $this->user_model->on(‘another_connection’)->get_all().

But what if, after we’ve used this so called “another_connection” we want to use the default connection? For this we will need another method. Let’s call it reset():

Setting the table

If we respect the “rules of engagement”, the table name should be easy to retrieve from our MY_Model, by getting the plural of the models that extend MY_Model without the “_model” or “_m” substring. Also, we allow the users to set their own table name.

So just before the constructor we define another protected property:

And inside the constructor we call a private function named _fetch_table() that will return the name of the table:

The function would look like this:

Setting the ID of rows

We will also allow the user to set up the column that will uniquely identify the row of data. But in the same time we will set it to be ‘id‘:

So, before the constructor we define a protected property $_primary:

The created_at, updated_at and deleted_at fields

In most cases when working with databases, admins need to know when was a table record created, modified or “deleted”. For these situations, we should make sure we offer the users of MY_Model the possibility to fill these columns on the fly.

So, let’s define one new property named $timestamps. If this is set to true, it will know that it must fill these columns with values when needed

But what if the user of our MY_Model names the columns differently than “created_at”, “updated_at” and “deleted_at”? Well, for that possibility we can allow the user to define them inside the model that extends MY_Model:

If we allow for this to happen, we should make sure we keep the fields name in our MY_Model. So, let’s define them before the constructor:

Now, inside the constructor we can make sure that if $timestamps is set to true or is an array, the model can find the accordingly fields in the table:

And now let’s define the method:

The insert() method

The insert() method should accept an array as first parameter which will contain the values that should be inserted, if the array is a multidimensional one then it will insert more than one row:

The before_create, after_create, before_update, after_update, before_soft_delete, after_soft_delete, before_delete, after_delete triggers

We’ve created the insert() method, but what about the “created_at” field? Where does this one come into play? Well… we should make a before_create trigger which must add the created_at field in the $data that is received by the insert() method. For this, we just copy the code from Jamie Rumbelow’s great base model…

First of all let’s define the triggers before the constructor:

After we’ve defined them, we must define a function called trigger() that will take care of triggering the before_create, after_create, before_update, after_update, before_soft_delete, after_soft_delete, before_delete, after_delete:

Now we define a function that will append a created_at field type to the data we are inserting:

As you can see, we first look if there already is defined a created_at field.

After we’ve done all this, we make sure that we call add_created() function before the creation of row(s). We do this inside the constructor, by adding it as another element of the $before_create array.

Returning to our insert() method…

Now, that we’ve created the triggers, we must modify the insert() method so that it will call before_create and after_create triggers:

And that’s it…

The update() method

On the same note, let’s create the update() method.

Before starting to create the update() method, let’s not forget about the updated_at field. For this we will create an add_updated() method that will be used by the update() method:

Now we call the method in a before_update trigger inside the controller:

The update() method will receive an array or object as first parameter, and the name of the identifying column as a second parameter. What do I mean by this? If you want to update a row (or more than one row) and you know its ‘title’ you can do something like this:

MY_Model will retrieve the value of ‘title’ column from the array and do a $this->db->where(‘title’,’My title’);

But what if we will allow more than one way of updating? We can allow the user to pass an array as second parameter which will be passed to the where method:

The where() method

We’ve updated the records… but what about a where() method that will make everything easier for us. We will create a method that will accept parameters and will return the $this object:

The delete() method

The delete() method is going to get a little more complex, because we will allow the users to have “soft deletes”, deletes that are not really deletes but only a fill of the deleted_at column.

First let’s deal with the deleted_at field. We start by creating the add_deleted() method:

Before the constructor we will set $soft_deletes to FALSE:

And in the constructor we will append the add_deleted function to the before_soft_delete trigger.

Now we can return to our delete() method which will receive a $where parameter that, if not NULL, will be handled by the where() method we’ve just created.

The get() method – returning one row…

Let’s now turn to our R in the CRUD: the “read” part. And we will start with the get() method, which will return only one row:

Pretty simple, right? If there is no $where parameter, it will return only the last row. But that doesn’t stop us to use the where() method inside the controller/model:

But let’s see how we can improve on what we have.

The limit() method

Why not, instead of using the query builder’s limit() method we map one of our own methods to it, so that it can be chained in our queries. So let’s create a method named limit(), which will receive two parameters: a limit of returned rows and an offset:

And now we simply replace $this->_database->limit(1)  with $this->limit(1)  in our get() method.

Return array or object?

Now, what if we want the returned result to be an array, and not an object? BUT what if we want for ALL our queries to return arrays?

For this we will first need to setup another property named $return_as which will accept as values either ‘array’ or ‘object’:

Now let’s create the methods that will allow us to change the way the results are returned:

Doing these functions will allow us to also chain the return type in our queries. Now we create a function that will return the result. Jamie Rumbelow’s MY_Model has a great method named _return_type() so we will just copy from him:

Now we go back to our get() method and call the above function on as result.

Returning to our get() method

Now, returning to our get() method, this is how it looks now:

Nice…

But what about the soft_deletes?

Well… the question here is: “If we have the $soft_deletes enabled, the read methods shouldn’t return the soft deleted rows unless asked for them, right?”

Laravel’s Eloquent has a few methods for the soft deletes: withTrashed(), onlyTrashed(), restore(), forceDelete(), trashed(). So why not make those in our MY_Model too?

The with_trashed() method

The with_trashed() method will make sure that the results will be returned with the soft deleted rows.

First of all we will create a property named $_trashed to ‘without’. This property can have three values to ‘only’ or ‘without’ or ‘with’, so that when a read query is made it will either show only the soft deleted rows, or only the not soft deleted rows or both.

Let’s first define the property before the constructor of MY_Model:

Now let’s create a with_trashed() method, an only_trashed() method:

Once we’ve done this, we should create a private method named _where_trashed() that will create one more where statement, and be called by the where() method:

Now we will go to our where() method and do a small change that will call this private method:

Great.

The restore() method

The restore() method will “undelete” one or multiple rows. What that means is that it will set the “deleted_at” value to NULL:

The force_delete() method

This method will force the delete of a row if soft_deletes is enabled. This means that it won’t just fill the row with a value at deleted_at field, but simply delete it:

The trashed() method

The trashed() method will verify if a row is “deleted”, soft deleted:

Wow… we’re moving faaaast!

The get_all() method

The get_all() method will return more than one result (if there is a result). We will change take over the code from the get() method for this one:

The fields() method

But what if we want only a few columns, and not all of them? For this it would be nice to do another method named fields(), to which we can pass, the fields names, either as a string with the fields separated by comma, or as an array:

Cool…

The order_by() method

Going further, why don’t we have an order_by() method? Let’s create it, or just copy it from Jamie:

What does this method do? if we pass it an array it will take the key as field name and the value as ascending (ASC) or descending (DESC) order. Else, if the first parameter is a string, it will accept a second parameter as the ordering type.

To be continued with RELATIONSHIIIIIPS

Now, let’s see the end result so far:

 

10 thoughts on “Revisiting MY_Model in CodeIgniter: Copying from Jamie Rumbelow and looking at Eloquent

  1. Tpojka

    Hello,

    I am trying to implement many to many relation, but as result I am getting only pivot_table_foreign_key and foreign_table_local_key in result set. In plain sql I would use something like:
    // $query = $this->db->query(“select articles.*, tags.* from articles_has_tags join articles on article_id = articles_article_id join tags on tag_id = tags_tag_id ORDER BY $order_by DESC LIMIT $limit”);

    // $query = $query->result();
    In respective models, pivot table and foreign keys are named as well using right way description.
    What should I do to get all data from foreign table?

    Reply
      1. Tpojka

        Thank you for reply.
        I found out that $this->_select doesn’t take "*" from some point. Also revealed that I can use fields argument in with_relationparameter() to get wanted values.
        In my project, I changed some code to be able to use native workbench created many to many tables that are named table1_has_table2 although it shouldnt be problem since I assigning specific names of tables and name of keys in models. I will try with default installation of MY_Model and let you know if it is working as well or I have some issues.

        Reply
        1. avenirer Post author

          It would be best to have the latest MY_Model, as I think the last thing I did was exactly that problem with the _select property…

          Reply
          1. Tpojka

            Sorry for bothering.
            It is all working well when I reassessed get_relate property. :blush:
            Btw, have you checked that line 1245 which doesn’t fit documentations?

            Best regards,

  2. Daniel

    How do use update method , when updating related data in 2 or more tables ,
    let’s say the user name , and phone? stored in 2 tables but related.

    Here https://github.com/avenirer/CodeIgniter-MY_Model i saw an example with only updating one table.
    How do i make my model to update let’s say
    Name, Phone number, prefix, Address, all inserted in 3 distinct tables?

    Reply
    1. avenirer Post author

      You can’t… or you can if you change the MY_Model accordingly. But this is not an ORM, and has its limitations.

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

No spam? * Time limit is exhausted. Please reload CAPTCHA.