Dependency Injection for CodeIgniter Models |
I'm looking for some ideas / good practices on injecting classes (possibly other CI models) and properties into CodeIgniter models
My goal is to create models that are "testable" (although I won't necessarily start testing every model, I feel "testable" models is a good practice to adhere to). For clarity, I'm referring to models that contain business logic. And I'm using CI3. At the moment I am trying out two different approaches (from a controller): Code: $this->load->model('some_model'); And Code: $this->load->model('some_model'); In general, my design preferences are:
Are there other ways to inject into CI Models? How do others handle it? I've Googled around and can't seem to find too much. Thanks so much! (07-07-2016, 05:03 AM)Narf Wrote: Thanks for your help. If I have application/models/Foo_model and then instantiate as you suggest I get: Code: Class 'Foo_model' not found If I load the model first then I get: Code: Missing argument 1 for Foo_model::__construct(); Because I have this in Foo_model: Code: function __construct($dependency) My understanding is that you can't inject into the __construct of a CI Model unlike with a Library. Is that right?
You can't use $this->load->model() to inject a dependency into a constructor. However, if you have an autoloader which can find your model or you manually load the model, you can inject the dependency when you instantiate the model.
If you wanted to use $this->load->model() and constructor injection, the constructor's argument would have to be optional, which means you would have to determine how you want to handle the case in which the dependency is not injected. Since the CI loader instantiates the model, it's not really the best way to handle loading a model if you intend to instantiate it yourself to inject a dependency. However, if the extra instantiation doesn't pose a problem in your application, you'll probably want to make sure you replace the instance on the CI singleton with your new instance, e.g. PHP Code: $this->foo_model = new Foo_model($dependency); Another alternative, which I would definitely NOT recommend, would be to extend the loader. One of the biggest problems I see with this is that I don't see a clear way to do it without duplicating a large amount of the code in the model() method, which leaves you with a lot of potential for maintenance issues down the line. Further, you'd probably want to create a new method, rather than overriding the existing model() method and adding a 4th parameter, otherwise you'd be forced to add the default values of the 2nd and 3rd parameters to your method calls when you are injecting dependencies. (07-07-2016, 06:59 AM)mwhitney Wrote: You can't use $this->load->model() to inject a dependency into a constructor. However, if you have an autoloader which can find your model or you manually load the model, you can inject the dependency when you instantiate the model. Thanks @mwhitney for your suggestions. I'll give it some thought. I'm not keen on the optional arguments, as to me that defeats the purpose.
The way i do this is by creating my own override of the model() method MY_Loader.php and copying in the model loading method from the core. Before the return on that method i add the following code:
PHP Code: $this->_ci_models[] = $name; Once that is in, put the MY_Loader.php in the application/core folder and then inside your model you can add an optional method called loadDependencies() which will be called when it's available: PHP Code: use Clubs\Club; In this example i am loading a dependency of /application/models/v3/Clubs/Club.php which has it's own namespace of "Clubs" and loads in the constructor it's own model: PHP Code: <?php So now in the main model when i do a database query i can return the result with this object: PHP Code: /** Ok the "use" statement is a bit ambiguous in this case...you can ignore it. Hope that helps. |
Welcome Guest, Not a member yet? Register Sign In |