Overview
As with quite a lot in technical subjects until you use something in a real way it quite often doesn’t really make sense. This was true of Dependency Injection for me. I have read about DI for years and didn’t really see the benefits in the projects I worked on. Now for most experienced developers or those who work on larger projects this stuff is basic common sense I am sure. But for most of us amateur or professionals who just do small projects it is not so clear. Up till now the benefits didn’t outweigh the added complexity. This post is about my light bulb moment with Dependency Injection in the real world and it’s practical benefits.
I am starting a personal project at the moment and choosing a framework was problematic. I wanted to learn a new framework and after some investigation decided not to use a new one for this project but do a separate project later for learning either Symfony or Laravel. That left me with a choice between Codeigniter 3 and Codeigniter 4. The problem with 4 is that Ion_Auth is not yet converted and I have lots of HMVC modules that rely on it. I did look at converting Ion Auth to CI4, but I just want to get on with this project.
My solution is to write the new project in such a way that I can convert it to CI4 relatively easily.
The way I am going to do this is to avoid having slim controllers and fat models. I am aiming to make both the controllers and models as slim as possible and use libraries for the bulk of the code. The things I want to avoid is having any hard dependencies on any specific framework in the main application code. Having slim controllers and models that only contain the bare minimum and only parts that are framework dependent makes transferring to a new framework in the future much easier.
So how have I decided to proceed?
Let’s take a single example. I wanted to rewrite my HMVC menu module into a simple library and make it more feature rich. This library however needs to access databases. I didn’t want to hardwire the Codeigniter 3 database as a dependency so here is what I did.
For the application controller which for now is just called Test. We have the following code.
<?php defined('BASEPATH') OR exit('No direct script access allowed'); class Test extends CI_Controller { function __construct() { parent::__construct(); } public function index() { $this->load->model('menu_model'); $config = array(); $params = array ( 'menu_model' => $this->menu_model, 'config' => $config, ); $this->load->library('menu', $params); $result = $this->menu->get_entry(); } }
This should be mostly self explanatory. We load a model as normal and then create an empty array for $config which will contain configuration data which will be loaded using the codeigniter standard call.
$this->config->load('filename');
Then we need to create a parameters variable and set the menu_model (which will provide the database functionality) and the configuration. These then get passed to the constructor.
The model looks like this for now. This is just a simple test of database access.
<?php defined('BASEPATH') OR exit('No direct script access allowed'); class Menu_model extends CI_Model { private $error; function __construct() { } public function get_menu_by_id($id) { $this->db->where('menu_header_id', $id); $query = $this->db->get('menu_items'); if ($this->db->affected_rows() == 0) { $this->error = "Sorry no menus with id: $id"; return (FALSE); } return ((array) $query->result()[0]); } }
In the library we have this code;
<?php class Menu { private $config; private $menu_model; public function __construct($params) { $this->menu_model = $params['menu_model']; $this->config = $params['config']; } public function get_entry() { return ($this->menu_model->get_menu_by_id('1')); } }
So putting it all together. We load the model for database access and pass this to the Library (dependency injection) Then in the library constructor we save this to
$this->menu_model
In the main functions when we want to access the database we use
$this->menu_model->function('params');
So when I eventually transfer this code to another framework I don’t have to rewrite the whole thing. I will just have to write a new model that is purely for database access. Converting each function in the model should be relatively straight forward. The menu code itself is unchanged.
The menu_model should only consist of a handful of CRUD like functions, whereas the library will have 20+ functions. Rewriting 5 simple functions in the model is going to be much easier than hunting for all the places in library code for any changes needed to get this to work with a new framework.
Final notes.
Please note I am not using namespaces here for two reasons. The first is I didn’t want to muddy the waters for the example and second CI3 doesn’t really support namespaces. Adding namespaces in the future will be really simple though.
As part of this project I will be making this a third_party module so that it can be developed and updated separately from the primary project and can be used in either native CI3 or CI3 with HMVC code easily.
I hope this helps.
Leave a Reply