CodeIgniter
Article by
on April 21, 2012, last modified on April 8, 2013CodeIgniter is the best PHP MVC framework available. It assumes nothing apart from the very basic essentials, such as routing and directory structure. It doesn't even autoload the database library. It's flexible enough that controllers don't even need models or views, and models have almost all the functionality of controllers.
Autoloading Classes
You can autoload classes by adding this little script to application/config/config.php
:
// deduce the application root from this file's location function __autoload ($class) { global $config; // ignore autoloading CodeIgniter classes $prefix = substr($class,0,2); if ($prefix!='CI' && $prefix!=$config['subclass_prefix']) { include(str_replace('_', DIRECTORY_SEPARATOR, strtolower($class)) . '.php'); } } // set the root of the application directory define('APP_ROOT', dirname(dirname(dirname(__FILE__)))); // put most likely paths first set_include_path('.' . PATH_SEPARATOR . APP_ROOT . '/application/libraries' . PATH_SEPARATOR . APP_ROOT . '/application/models' . PATH_SEPARATOR . APP_ROOT . '/application/controllers' . PATH_SEPARATOR . get_include_path() );
I have to credit the technique from Gerard Sychay's Pox Framework. The __autoload
function is like a magic method in that when a class is not found, the name of the class being looked for is sent as a parameter and the function is given the option of finding the file that has the class. Finally, to expand the scope of directories the autoload function has to look in we add the library, model, and controller folders to PHP's include path using set_include_path
.
Now, three quick things to note about what the autoload function is doing:
- Classes beginning with 'CI' and the 'subclass_prefix' are ignored because we don't want to mess with the core of CodeIgniter
- The class name is changed to lowercase because lowercase filenames are CodeIgniter's convention
- A class name of 'Post_Comment' will be looked for in the following locations:
- application/libraries/post/comment.php
- application/models/post/comment.php
- application/controllers/post/comment.php
- (and any in any other include path)
Using this technique allows you do things like:
class User extends MyModel
Where MyModel
is in application/models/mymodel.php
.
Error Handling
Handling Form Errors
There are many ways to get form errors (see this StackOverflow thread and this one) including the built in form_errors function. The easiest way, I believe is to do the following:
$validator =& _get_validation_object(); $error_messages = $validator->_error_array;
Try-Catch Method
Best Practices (Introduction)
There is no de facto way to handle errors in CodeIgniter so I'm going to extrapolate what I think the best is. The best way is to throw and catch PHP exceptions. For error reporting on the same request you cannot use the session flashdata.
Handling Errors in Controllers
As a post on StackOverflow shows, try and catch calls to models in your controller:
try { $this->mymodel->insert(); } catch (Exception $e) { $error = $e->getMessage(); }
Handling Errors in Models
For any error we want to throw an exception so that we can catch it in the controller. For example, if a required field is empty you may do the following:
if (!$this->input->post('name')) { throw new Exception('Name is a required field'); }
Yet, we have another problem. By default, CodeIgniter handles database errors and throws them to the user. To get around this we will need to take a few steps:
1. Turn off showing errors
As a CodeIgniter forum comment explains, edit your database config file to set debug to false:
$db['default']['db_debug'] = FALSE;
2. Capture the error
And another forum comment explains that you can catch the error using this method:
$error = $this->db->_error_message();
3. Throw it again
Then, to send the error back to the controller, we will need to throw the error again:
if ($error) { throw new Exception($error); }
If you need, you can also get the MySQL error number as shown in a StackOverflow comment using $this->db->_error_number()
.
Showing Errors in Views
Finally, to show these errors in your view you can do any number of things, but I have found a convention that works well. I will let the code do the talking:
application/controllers/mycontroller.php: try { $this->mymodel->insert(); } catch (Exception $e) { $error = $e->getMessage(); } $this->view('t/header'); $this->view('mycontroller/myform'); $this->view('f/footer'); application/views/t/header.php: $this->view('t/messages'); application/views/t/messages.php: <?php if (isset($info) || $this->session->flashdata('info')) { ?> <div class="notification information canhide"> <p><strong>INFORMATION: </strong> <?php echo isset($info) ? $info : null; ?> <?php echo $this->session->flashdata('info'); ?> </p> </div> <?php } ?> <?php if (isset($notice) || $this->session->flashdata('notice')) { ?> <div class="notification lightbulb canhide"> <p><strong>NOTIFICATION: </strong> <?php echo isset($notice) ? $notice : null; ?> <?php echo $this->session->flashdata('notice'); ?> </p> </div> <?php } ?> <?php if (isset($warning) || $this->session->flashdata('warning')) { ?> <div class="notification warning canhide"> <p><strong>WARNING: </strong> <?php echo isset($warning) ? $warning : null; ?> <?php echo $this->session->flashdata('warning'); ?> </p> </div> <?php } ?> <?php if (isset($success) || $this->session->flashdata('success')) { ?> <div class="notification success canhide"> <p><strong>SUCCESS: </strong> <?php echo isset($success) ? $success : null; ?> <?php echo $this->session->flashdata('success'); ?> </p> </div> <?php } ?> <?php if (isset($error) || $this->session->flashdata('error')) { ?> <div class="notification failure canhide"> <p><strong>FAILURE: </strong> <?php echo isset($error) ? $error : null; ?> <?php echo $this->session->flashdata('error'); ?> </p> </div> <?php } ?>
Cons to This Method
The cons to this method is that it is not granular. For example, if you wanted to display an error box next to a specific form input you would have to read the error message in the controller and assign a specific variable and send it to the view, or something related.
A suggestion to deal with this issue would be to make the error message a JSON object with keys corresponding to specific types of errors, say an error for a specific form input. If you set up the convention well enough you may even be able to easily integrate your model error messages with an API if your application has one.
References
http://stackoverflow.com/a/6231551/990642
http://stackoverflow.com/questions/2705391/best-practice-for-error-handling-in-codeigniter-php-apps
http://stackoverflow.com/questions/734138/best-practices-for-processing-errors-from-database-in-codeigniter
http://us2.php.net/exceptions
http://codeigniter.com/user_guide/libraries/sessions.html
http://codeigniter.com/user_guide/database/configuration.html
http://codeigniter.com/forums/viewthread/106742/#537198
http://codeigniter.com/forums/viewthread/76524/#403329
Models
Scaffolding
Scaffolding makes CRUD (Create Read Update Delete) operations much easier to write. There are a few scaffolding libraries out there for CodeIgniter, one StackOverflow post lists some of these as well:
- CI Scaffold - http://code.google.com/p/ci-scaffold
- iScaffold - http://iscaffold.skyweb.hu (or on GitHub)
- Sparkplug - http://code.google.com/p/sparkplug
Long story short, I have not tried any of these libraries, but my reaction is that they are not worth looking into. Nonetheless, here are my comments.
CI Scaffold looks like a great option and easy to implement. However, they give you a full instance of the CodeIgniter framework and having to dig through the examples to figure out what you need and don't need is a pain. Likewise, iScaffold is a beast and looks very difficult to implement. They too offer an entire instance of the CodeIgniter framework that they have modified. Last but not least, Sparkplug, which was inspired by Rails, is just one file and may be easy to implement, but it looks like the Sparkplug.php file is just a template for what your controller should look like.
Active Record Models
http://gustavostraube.wordpress.com/2010/11/24/model-instances-with-active-record-on-codeigniter/
Authentication
There is a HUGE list of libraries out there. So, there is no point in me re-writing the list. Also, there is a great thread on StackOverflow that compares the auth libraries.