Better Controllers and Models in CodeIgniter
CodeIgniter is an expressive, flexible gorgeous web application framework, but it is often a little too simple for my personal convenience. In my recent talk at EECI I talked about my experiences with Ruby on Rails and how the PHP and CodeIgniter community could learn from it, and showed off my MY_Controller.php and MY_Model.php classes. I’ve had a few people ask me about them and so I thought I’d release them and write a tutorial here so that you guys can speed up your development and write better applications.
Getting hold of the libraries
To follow this tutorial, or the various parts, you’ll need to download and install both core libraries. You can get hold of both of them from GitHub, or download the MY_Controller and MY_Model files directly. After you’ve got the code, just drag and drop the files into your application/libraries folder and you’re good to go.
To take advantage of both the libraries you have to to extend your controllers and models from MY_Controller and MY_Model respectively. This is the only way we can have a global extension layer without hacking the core code directly.
Loading views by convention
The first addition the MY_Controller makes to your controllers is a convention-based auto view loading mechanism. It automatically loads the view based on the controller name and the action currently invoked by the routing system. The application/views directory is split up into subdirectories based on the controller name. It will then try to load the name of the action as a view.
So, let’s look at an example. If we load up the Posts controller and hit the index() action, the system will run the Posts::index() action and then look for the application/views/posts/index.php view. It’s simple! If you want to pass any data to the view, you can set the $this->data array like you would normally, and it will be passed straight through to the view!
Nested Layout Support
The next very cool functionality you get is support for layouts. Again, this is convention based but it can be customised as much as you like. The layout support automatically loads the view into the layout, passing all variables through and making the view rendered in a variable for display in your layout.
The system will first check that the $this->layout variable is set. If it is, it will load that layout. This allows you to put specific actions in different layouts. It will then look for a layout file held in application/views/layouts with the same name as your controller - useful for when you’ve got a admin controller, for example, that needs a different layout for all it’s actions. If this doesn’t exist the class will finally load application/views/layouts/application.php, which can be used as a global application layout.
The action’s view will be passed through to the layout in a variable called $yield. You can manipulate this as much as you like, but remember to output it!
Asides and Partials
If you want to render a sidebar in your layout that needs to be changed in some of your controllers, you can use the asides support. Just set the class variable $this->asides to an array with the key as the name to use internally (a variable safe name, so only letters, numbers and underscores) and the value as the file. It will then be passed into the layout as a variable with the name, prefixed with $yield_. A quick example:
We also have partial support here - useful for repeated code in your views. The class gives you a helper method to use in your views called partial() and this takes three arguments. The first argument is the name of the partial - the name of the view to load relative to application/views. The second argument is an optional array of data to pass through. The third, and most interesting, is a boolean to loop through a collection.
If you have an array from a database query (ala $query->result()), full of objects that contain data, you can pass that directly to the second parameter, and if you pass TRUE as the third parameter, the system will loop through that collection, passing through each object and rendering the view for each iteration. This makes it really easy to clean up your views and spares you from writing lots of foreach loops!
Auto-model loading
The last helpful addition the MY_Controller gives you is quite a cool little convention of loading models. All you have to do is set the $this->models class variable in your controller to be a list of the models you want to load (following the naming convention, of course), and the system will load the model. It will assume that the model has _model appended to the file and class name, but will load it into the name that it’s under in the array. For instance, if my $this->models array had a ‘game’ model, it would look for the file and class Game_model but will load it to $this->game.
The Base Model
The base Model that I use provides a nice interface to each model that supports the basic CRUD functions, as well as a few other helpful niceties. It’s very simple to use and understand, and once you’re using it, you won’t want to go back!
CRUD Functions
The base Model contains 16 CRUD methods that are automatically available to your models that perform the most basic create, read, update and destroy database tasks. The following is a total list of the functions and parameters they take, but I’d recommend looking through the source to see how they work. They all use CodeIgniter’s ActiveRecord library so it’s not tied to any database driver specifically, and you still gain the other benefits of using AR.
Naming Conventions
This class will try to guess the name of the table to use, by guessing the plural of the class name. If the table name isn’t the plural and you need to set it to something else, just declare the $table instance variable and set it to the table name. Some of the CRUD functions also assume that your primary key ID column is called ‘id’. You can overwrite this functionality by setting the $primary\_key instance variable.
Before and After Create Callbacks
If you need to manipulate the data that you’re inserting into the database using the insert() method, you can make use of the before and after create callbacks built into the system. Just create a method in your model to do the processing and set the $this->before_create or $this->after_create variables to contain the method (it’s expected to be an array of strings).
Before create callbacks take one parameter (the data about to be inserted) and can then manipulate the data as much as they like, before returning it for insertion. After create callbacks don’t return anything, but take two parameters, the first, the data and the second, the primary key that’s returned from insertion.
Upcoming
I’ve got plenty of ideas of where to go next with these libraries, and I’ve already had some help from some awesome people, but I’d love to hear your thoughts and ideas. What do you think of the current systems? How could they be improved? What would you like to see? I’ll be updating the GitHub repo with any changes I make so do keep an eye on that, but I’m sure to tweet about it too. Enjoy!