Jens Segers on Jan 04 2012

CodeIginter HMVC Modules

This extension for CodeIgniter enables the use of the Hierarchical Model View Controller(HMVC) pattern and makes your application modular. This allows easy distribution of independent components (MVC) in a single directory across other CodeIgniter applications. All modules are grouped in their own folder and can have their own controller, model, view, library, config, helper and language files.

I used to use the 'Modular Extensions - HMVC' by wiredesignz, but when the original author changed something I did not like, I took matters in own hands. I actually found an easier way to enable HMVC support unlike the original code. Since the introduction of CodeIgniter 2.0 there has been support for packages built right inside CodeIgniter. This extension utilizes this but adds the extra functionality you need for full HMVC support. Therefore it was not needed to change the main CodeIgniter core files, but only wrap them with package functions. This is a real advantage over the original code that did change a lot of core files.

Installation

Download the file from github and pace them into their corresponding folders in the application directory.

Note: to use the HMVC functionality, make sure the Controller and Router you use extend their HMVC class.

Next, add the location of your modules directory to the main config.php file:

/*
|-------------------------------------
| Modules locations
|-------------------------------------
|
| These are the folders where your modules are located. You may define an
| absolute path to the location or a relative path starting from the root
| directory.
|
*/
$config['modules_locations'] = array(APPPATH . "modules/");

Functionallity

This is the basic structure of a HMVC module:

# /modules
#    /module
#       /controllers
#       /config
#       /helpers
#       /language
#       /libraries
#       /models

From within a module you can load its own resources just like you always do. If on the other hand, you want to load resources from another module you can do this by adding the module's name like a directory structure:

class Hello extends MY_Controller {

    public function index() {
        // load a model from the current module
        $this->load->model("local_model");

        // load a model from another module
        $this->load->model("other_module/model");

        // HMVC example
        $this->load->controller("module/controller/method");
    }
}

Because of the modified router, the module's controllers are accessible like a directory structure. Controllers may be loaded from the application/controllers sub-directories or the module/controllers sub-directories:

# /module/hello -> /module/controllers/hello.php (index method)
# /module/hello -> /module/controllers/hello/hello.php (index method)
# /module/hello -> /module/controllers/hello/(default_controller).php (hello)
# /module/hello -> /module/controllers/module.php (hello method)
# /module/hello -> /module/controllers/(default_controller).php (hello method)

If the requested module contains a routes.php config file it will automatically be added to the main routes.

Hierarchical controllers

To load hierarchical controllers you use the $this->load->controller() method. This method works similar to loading views with CodeIgniter, this method accepts the following parameters:

  • URI: a URI string pointing to the requested controller (and method). This function uses the same locating technique as explained above.
  • Parameters: an array containing the arguments for the requested method.
  • Return : a boolean indicating whether the output should be returned or show on the screen (default).

For example, this is our main controller where we pass the request to a sub-controller in the same module:

class Specials extends MY_Controller {

    public function index() {
        $this->load->controller('blogs/random', array('specials'));
    }

}

And the sub-controller contains the real method:

class Blogs extends MY_Controller {

    public function random($type) {
        ...
    }

}

You can also capture the output generated by the sub-controller by using the third $return parameter

$this->load->controller('blogs/random', array('specials'), TRUE);

More about HMVC:

http://en.wikipedia.org/wiki/Presentation-abstraction-control
http://techportal.ibuildings.com/2010/02/22/scaling-web-applications-with-hmvc/


Comments

Fefo 5 months ago

Hi Jens, thx for your work. I've set up a brand new copy of Codeigniter 3.1.9. Downloaded (at different times) both your HMVC code and wiredesignz MX code and everything went like a charm. As soon as I install Ion_auth 2, everything works fine unless I use (either) $this->load->controller() of Modules::run, from an 'application/controlllers' calling a module controller. On it's own, the module controller works fine (as well as without Ion_auth being installed (even if called from an 'application/controllers'. Whenever I try to execute a modules' controller, I get the infamous "Unable to locate the specified class: Session.php" message, and nothing get's executed past that. Fiddling with log_message, I managed to find the line of problem to MX_Modules' "self::$registry[$alias] = new $controller($params);" -- (in your HMVC Loader: $this->_ci_controllers[strtolower($class)] = new $class(); in _load_controller function).

Any thought that can be helpful will be greatly appreciated.

The log, throws the following:

DEBUG - 2018-07-08 16:37:15 --> Initiating MY Log class extension DEBUG - 2018-07-08 16:37:15 --> UTF-8 Support Enabled DEBUG - 2018-07-08 16:37:15 --> (MX Modules -> parse_routes()) Parsing routes for module: welcome with uri: welcome// DEBUG - 2018-07-08 16:37:15 --> (MX Modules -> find()) Finding file: routes with module: welcome and base: config/ DEBUG - 2018-07-08 16:37:15 --> No URI present. Default controller set. DEBUG - 2018-07-08 16:37:15 --> Global POST, GET and COOKIE data sanitized DEBUG - 2018-07-08 16:37:15 --> (MX Loader -> initialize()) Initializing parent controller of DEBUG - 2018-07-08 16:37:15 --> (MX Loader -> helper()) Loading module helper: url DEBUG - 2018-07-08 16:37:15 --> (MX Loader -> helper()) Loading module helper: form DEBUG - 2018-07-08 16:37:15 --> (MX Loader -> helper()) Loading module helper: cookie DEBUG - 2018-07-08 16:37:15 --> (MX Loader -> helper()) Loading module helper: language DEBUG - 2018-07-08 16:37:15 --> (MX Modules -> find()) Finding file: url_helper with module: null and base: helpers/ DEBUG - 2018-07-08 16:37:15 --> (MX Modules -> find()) Finding file: form_helper with module: null and base: helpers/ DEBUG - 2018-07-08 16:37:15 --> (MX Modules -> find()) Finding file: cookie_helper with module: null and base: helpers/ DEBUG - 2018-07-08 16:37:15 --> (MX Modules -> find()) Finding file: language_helper with module: null and base: helpers/ DEBUG - 2018-07-08 16:37:15 --> (MX Modules -> find()) Finding file: Session/session with module: null and base: libraries/ DEBUG - 2018-07-08 16:37:15 --> Full path: /Library/Server/Web/Data/Sites/hmvc.fefo.test/application/modules/Session/libraries/Session.php with offset: ../modules/ and base: libraries/ DEBUG - 2018-07-08 16:37:15 --> (MX Modules -> find()) Finding file: session with module: null and base: config/ DEBUG - 2018-07-08 16:37:15 --> (MX Modules -> load_file()) Loading file: Session with path: /Library/Server/Web/Data/Sites/hmvc.fefo.test/application/modules/Session/libraries/ DEBUG - 2018-07-08 16:37:15 --> File loaded: /Library/Server/Web/Data/Sites/hmvc.fefo.test/application/modules/Session/libraries/Session.php DEBUG - 2018-07-08 16:37:15 --> (MX Loader -> database()) Loading database drivers with params: DEBUG - 2018-07-08 16:37:15 --> (MX Loader -> libraries()) Loading module library: form_validation DEBUG - 2018-07-08 16:37:15 --> (MX Modules -> find()) Finding file: form_validation with module: null and base: libraries/ DEBUG - 2018-07-08 16:37:15 --> (MX Modules -> find()) Finding file: form_validation with module: null and base: config/ DEBUG - 2018-07-08 16:37:15 --> (MX Modules -> find()) Finding file: form_helper with module: null and base: helpers/ DEBUG - 2018-07-08 16:37:15 --> (MX Loader -> libraries()) Loading module library: ion_auth DEBUG - 2018-07-08 16:37:15 --> (MX Modules -> find()) Finding file: ion_auth with module: null and base: libraries/ DEBUG - 2018-07-08 16:37:15 --> (MX Modules -> find()) Finding file: ion_auth with module: null and base: config/ DEBUG - 2018-07-08 16:37:15 --> (MX Modules -> find()) Finding file: ion_auth with module: null and base: config/ DEBUG - 2018-07-08 16:37:15 --> Config file loaded: /Library/Server/Web/Data/Sites/hmvc.fefo.test/application/config/ion_auth.php DEBUG - 2018-07-08 16:37:15 --> (MX Loader -> libraries()) Loading module library: email DEBUG - 2018-07-08 16:37:15 --> (MX Modules -> find()) Finding file: email with module: null and base: libraries/ DEBUG - 2018-07-08 16:37:15 --> (MX Modules -> find()) Finding file: email with module: null and base: config/ DEBUG - 2018-07-08 16:37:15 --> (MX Modules -> find()) Finding file: ion_auth_lang with module: null and base: language/english/ DEBUG - 2018-07-08 16:37:15 --> (MX Modules -> find()) Finding file: ion_auth_model with module: null and base: models/ DEBUG - 2018-07-08 16:37:15 --> (MX Modules -> find()) Finding file: ion_auth with module: null and base: config/ DEBUG - 2018-07-08 16:37:15 --> (MX Modules -> find()) Finding file: cookie_helper with module: null and base: helpers/ DEBUG - 2018-07-08 16:37:15 --> (MX Modules -> find()) Finding file: date_helper with module: null and base: helpers/ DEBUG - 2018-07-08 16:37:15 --> (MX Modules -> find()) Finding file: ion_auth_lang with module: null and base: language/english/ DEBUG - 2018-07-08 16:37:15 --> (MX Loader -> database()) Loading database drivers with params: DEBUG - 2018-07-08 16:37:15 --> (MX Modules -> find()) Finding file: bcrypt with module: null and base: libraries/ DEBUG - 2018-07-08 16:37:15 --> (MX Loader -> _add_module_paths()) Adding module paths for module: DEBUG - 2018-07-08 16:37:15 --> (Welcome controller -> index()) DEBUG - 2018-07-08 16:37:15 --> (MX Modules -> run()) Running module: serviciosespeciales with method index DEBUG - 2018-07-08 16:37:15 --> (MX Modules -> load()) Loading module: serviciosespeciales DEBUG - 2018-07-08 16:37:15 --> (MX Modules -> load()) Module was not in Registry - with alias: serviciosespeciales DEBUG - 2018-07-08 16:37:15 --> (MX Modules -> parse_routes()) Parsing routes for module: serviciosespeciales with uri: serviciosespeciales DEBUG - 2018-07-08 16:37:15 --> (MX Modules -> find()) Finding file: routes with module: serviciosespeciales and base: config/ DEBUG - 2018-07-08 16:37:15 --> (MX Modules -> load()) Module has class: serviciosespeciales DEBUG - 2018-07-08 16:37:15 --> (MX Modules -> load()) Module has path: /Library/Server/Web/Data/Sites/hmvc.fefo.test/application/controllers/../modules/serviciosespeciales/controllers/ DEBUG - 2018-07-08 16:37:15 --> (MX Modules -> load()) Loading the controller class: serviciosespeciales DEBUG - 2018-07-08 16:37:15 --> (MX Modules -> load_file()) Loading file: Serviciosespeciales with path: /Library/Server/Web/Data/Sites/hmvc.fefo.test/application/controllers/../modules/serviciosespeciales/controllers/ DEBUG - 2018-07-08 16:37:15 --> File loaded: /Library/Server/Web/Data/Sites/hmvc.fefo.test/application/controllers/../modules/serviciosespeciales/controllers/Serviciosespeciales.php DEBUG - 2018-07-08 16:37:15 --> (MX Modules -> load()) Class Serviciosespeciales loaded DEBUG - 2018-07-08 16:37:15 --> (MX Modules -> load()) Attempting to instantiate Serviciosespeciales with no params DEBUG - 2018-07-08 16:37:15 --> self::$registry[$alias] = new $controller($params); DEBUG - 2018-07-08 16:37:15 --> self::$registry[serviciosespeciales] = new Serviciosespeciales();


Rajan Acharya 2 years ago

What is the benefit of using class Blogs extends MY_Controller instead of using class Blogs extends CI_Controller as extending class CI_Controller works too? Thanks.


Hossein 3 years ago

It works on Codeigniter 3 :-)


Sergio 4 years ago

Hi Jens,

I use HMVC withouh problem with Apache.

Now, I am testing NGINX and your HMVC have problems with rules of NGINX.

ouhhh

:(


KobusKobus 5 years ago

Hi Jens,

I have opened MY_Loader and MY_Router PHP files in core, and they are extending the HMVC_Loader and HMVC_Router respectively. I followed your instructions above, and still get the problems. What else could I try, please? Thank you in advance :-)

Kobus


Jens Segers 5 years ago

@Kobus, you should check if the loader CI is using is the HMVC loader. It has been a while since I used the HMVC myself.


Kobus 5 years ago

Hi Jens,

I have up to now been using Wiredesignz' HMVC, and am trying to implement yours in its place, due to the fact that the routes does not work in WD's version.

I am, however, unable to get yours to work, and I am sure I am making a VERY STUPID mistake, but I would appreciate if you could help.

Here is what I have done:

  1. Downloaded your HMVC
  2. Placed the files in their corresponding folders inside APP folder
  3. Added the modules config item in my config file
  4. Opened module and changed the extending of "MX_Controller" to "CI_Controller" (I think this is where my problem lies - but do not know how to fix it).

When I do call "load->controller()" I get the error:

Fatal error: Call to a member function controller() on a non-object in path\to\mytestapp\application\hooks\replace_holder_hook.php on line 16

Lines 15 & 16 in that file are these:

$ci =& get_instance(); $ci->load->controller('core');

I am sure I am just missing somethign frfom your instructions, somehow. Please assist if you can, or let me know if you need more information?

Thanks!

Kobus


Juanan 5 years ago

It's done Jens! Working fine! Thanks a lot!


Jens Segers 5 years ago

@Juanan, I updated the code on Github. Could you test if it is working now? (https://github.com/segersjens/CodeIgniter-HMVC-Modules/commit/beb25540f2950e4fc897d96cf5afed43af514dba)


Juanan 5 years ago

Hi Jens, thanks & congratulations for your great work. I'm newbie with CI and i'm just starting with templating and HMVC as well. Here it goes my, perhaps stupid, question: i'm loading a module controller to a partial this way: $this->template->prefooter = $this->load->controller("hmvcsample/sample",array(),TRUE);

hmvcsample is the module and sample a method, obviously.

This works fine but my doubt appears when i'm forced to specify the module name when i load a view in the sample controller: $this->load->view("hmvcsample/sample_view");

Is this correct? I works fine but, shouldn't it be enough with: $this->load->view("sample_view"); which it's not working????

I thought views inside modules were resolved by its own... or perhaps i have some misconfiguration...

Thanks in advance and congratulations again.


Jens Segers 5 years ago

@Cyiza Rwanda, I am not sure if this will work. I should test it out sometime.

@desta, The prefixes can indeed cause some confusion at first, but there is no way around it :)


Cyiza Rwanda 5 years ago

Thanks boss... this is very massive, is there a way i can create multiple levels of modules? like being able to create a sub-module within a module? and then be able to create another module within that sub-module? I am working on a deep website... so i need either multiple submodules or multiple sub-controllers, which apparently CI can only support one level of controllers in a folder...


desta 5 years ago

i'm new really in HMVC, and i'm fall in love with it, so simple and understanding structure, but i'm confusing with prefix MX_blabla and CI_blabla it's worksing always. :)


barpinteihp 6 years ago

I think everyone like this blog!


Jens Segers 6 years ago

@Thiago, sure you can. Just make sure your own controller extends de HMVC one.


Thiago 6 years ago

Is it possible to extend my own controller?


Jens Segers 6 years ago

That's a bit overkill. I think the only way that could work if you add that second modules folder to $config['modules_locations'].


Jaume MK 6 years ago

hi, it's me again!I've been doing some more tests on this HMVC...I've tried to put another 'modules' folder inside a module... like this:—application——controllers——modules———hello————controllers————modules—————bye——————controllers———————bye.phpI'm trying to access 'bye'... would be that possible?localhost/index.php/hello/bye does not work... :S


Jens Segers 6 years ago

Let me know if you run into any problems!


Vikrantsharma 6 years ago

Thanks for answering. Actually the HMVC by wiredesignz does not work with most of the template libraries. This is the reason I asked this question. I will try yours and hope that it works.


Jens Segers 6 years ago

There are no changes to the original CI code, so I would not see any problems with template libraries.


Vikrantsharma 6 years ago

Hi jenssegers, does this library work with any of the templates library available for CI? Please confirm.


Jens Segers 6 years ago

No problem :)


Jaume MK 6 years ago

ok... forget that 'stupid' question... xDDD sorryjust get that thing working fine... misspelling some characters!!!thank u again!


Jaume MK 6 years ago

Hi, i apreciate the simplicity of this third party modular/HMVC, as i'm just now getting familiar with CI.I was wondering, if its possible to load/run a module/controller (or module/model) from the main CI controllers folder.—application——controllers———home.php (run hello controller from here)——modules———hello————controllers—————hello.phpIs this possible?Thanks for ur code! it will help me a lot on my CI Apps


Name user 6 years ago

Ok, thank you for the information. I will charge the data another way. Anyway thank you very much, it works better than Wiredesignz !


Jens Segers 6 years ago

I don't understand your question, could you clarify your problem a bit more?


Jens Segers 6 years ago

This is because both controllers extend MY_Controller. When another controller method is called it needs to create the object, so the constructor is called twice. I don't see any way around this at this moment.


Name user 6 years ago

It's me again :sWhen using $this->load->controller('xxx/yyy'), MY_Controller is called twices?i'm using this in MY_Controller: $this->load->library('profiler'); $this->output->enable_profiler(TRUE); Console::log('MY_Controller Initialized @ '.time());and in my log i can see :log MY_Controller Initialized @ 1331883581

                        log                         MY_Controller Initialized @ 1331883581There is a solution to this?tks.

Name user 6 years ago

If I use a "function index()" and another, "_gerenate()" in my controller and I remember the function generate in index function, it does not work. ex: [code]public function index(){if(blabalbala) // some code here …else $this->_generate();}public function _generate(){ return $this->superstar_model->………;}[/code]so I reorganize differently.


Name user 6 years ago

Thanks.It works great.