The Anatomy of a Modern CodeIgniter Application

The title might seem like a bit of an oxymoron but the reality is that shitloads of projects are still running on CodeIgniter. A quick look at Google Trends shows that CodeIgniter is still searched more often than Symfony and that it was only taken over by Laravel in August 2014.

I'd wager that more often than not that the developers maintaining these projects have fantasies about doing a complete rewrite in Laravel or have some other vague ideas of slowly porting over. Well, in this post I'm hopefully going to show you simple things you can do to drag your CodeIgniter application kicking and screaming into 2015.

In September of this year I joined a project which was built on CodeIgniter. Now this was a real CodeIgniter application - no Composer, no auto-loading, no namespacing, no ORM, no tests and a lot of use of CodeIgniter's built in classes. It was all very familiar.

The word "rewrite" did spring to mind - it would have been lovely but that's not why I was brought onboard. I only had a very short time add new features to this existing codebase, so whatever improvements I made needed to be things that were quick to introduce and didn't hamper progress of the project.

You're never going to be able to get CodeIgniter to act like a true modern framework, but with a little bit of persuasion and some good practices you can at least make it feel a lot less stone age.

RESTful Controllers

One of the things I needed to develop for this project was a RESTful API. REST isn't supported natively by CodeIgniter so some outside help was needed here. Luckily Phil Sturgeon created this library way back in the day and now Chris Kacerguis maintains it. Lovely.

CodeIgniter 3 Migrations

One of the most annoying things I found when regularly developing with CodeIgniter was its migrations. They were fine(ish) if you were one developer working on just one branch, but due to the incremental nature of its naming scheme it quickly lead to problems if you weren't.

CodeIgniter 3 fixes this issue by introducing timestamps in the migration names - similar to how Doctrine does it. This alone was enough of a draw for me to upgrade this application from 2.1, but we had other reasons too - including wanting to use the latest version of the REST library mentioned above.

Upgrading wasn't without its pains. The jump from 2 to 3 was a lot more difficult than I remember the jump from 1.7 to 2 being all those years ago. The biggest annoyance was the change in return values in the input class. For example in CodeIgniter 2, this code use to resolve as FALSE:

if($this->input->get('someshitthatdoesntexist')) {  
    // do something 
}

But in CodeIgniter 3 it resolved as TRUE. Oh fuck off. It's probably more logical, but it results in a lot of controller tweaking and was by far the biggest change.

Composer

If your CodeIgniter app isn't using Composer it's likely that you have an application/libraries/ directory that is some 'interesting' mix of your own custom classes and some third party ones. You might even be using some third party libraries written specifically for use with CodeIgniter. And you might even be loading some of these libraries like this:

$this->load->library('inane_lib');

I'm not going to go into the details of implementing Composer, you can find that out elsewhere. But once you do - I give you this advice:

  • Check Packagist for the libraries you app is currently using. It's likely most of them will be available there. If so, require them via Composer instead.
  • Use psr-4 autoloading for any new libraries you create. Get out of that CodeIgniter loading mentality.
  • And a bit of general advice on third party libraries - most of the time, if you see a some code purporting to be a "CodeIgniter" library - back away slowly and try find a non-framework-specific library on Packagist instead.
  • When you can, avoid using CodeIgniter's own built-in libraries and use Composer based libraries instead - just choose them wisely.

If you follow this advice, pretty soon you'll realize that the fact you're using CodeIgniter matters less and less. If you wanted you could even get to a point where you're just using CodeIgniter's routing and not much else. From there it would be easy to switch to a different framework if that's your ultimate goal.

Tests

Testing and CodeIgniter aren't exactly two words that go hand in hand. The project I joined didn't have any in place and I wasn't surprised at all.

As the new RESTful API I was building was just harnessing pre-existing code I decided that the best thing to do was write some acceptance tests with Behat. I highly recommend chapter 5 of this book if you're interested in doing the same. Actually, if you're building an API I recommend you read the whole thing. I've found it hugely beneficial over the last couple of months.

It's also worth noting this library which easily allows you to implement PHPUnit into your CodeIgniter application.

These are some improvements that I implemented and that you can implement now too. But you don't have to stop here. There are a bunch of other improvements that I want to introduce to my project over the coming months. Namely:

ORM

CodeIgniter doesn't have a built-in ORM and although there was a good third party ORM for CodeIgniter, it doesn't seem to be actively maintained anymore and doesn't work at all with CodeIgniter 3. So, I would recommend using Laravel's Eloquent. To implement it in CodeIgniter is actually fairly straight forward.

Firstly, require it via Composer:

$ composer require illuminate/database

Add this at the bottom of your application/config/database.php:

$capsule = new \Illuminate\Database\Capsule\Manager();

$capsule->addConnection(array(
    'driver' => 'mysql',
    'host' => $db['default']['hostname'],
    'database' => $db['default']['database'],
    'username' => $db['default']['username'],
    'password' => $db['default']['password'],
    'charset' => 'utf8',
    'collation' => 'utf8_unicode_ci',
    'prefix' => $db['default']['dbprefix'],
));

$capsule->setAsGlobal();
$capsule->bootEloquent();

And then create yourself some models:

use \Illuminate\Database\Eloquent\Model as Eloquent;

class User extends Eloquent  
{

}

Okay, so you've successfully dragged your CodeIgniter app into 2015. Give yourself a medal (nobody else will!).