Skip to content
World Wide Web Server edited this page Jul 4, 2012 · 85 revisions

This is a solution for people that are looking for a way to keep associated models, views and controllers in the same directory and thereby making their application more modular.

[h2]Installaion[/h2]

It uses two extended core classes; [b]MY_Loader[/b] and [b]MY_Router[/b], both of which should be placed in your [b]application/libraries/[/b] folder.

You will also need to create the [b]application/modules/[/b] folder.

[b]application/libraries/MY_Loader.php[/b] [code]<?php if (!defined('BASEPATH')) { exit('No direct script access allowed'); }

class MY_Loader extends CI_Loader { function model($model, $name = '', $db_conn = false, $module = '') { if ($model == '') { return; }

    if (strpos($model, '/') === false) {
        $path = '';
    } else {
        $x = explode('/', $model);
        $model = end($x);
        unset($x[count($x) - 1]);
        $path = implode('/', $x) . '/';
    }

    if ($name == '') {
        $name = $model;
    }

    if (in_array($name, $this->_ci_models, true)) {
        return;
    }

    $ci =& get_instance();

    if (isset($ci->$name)) {
        show_error('The model name you are loading is the name of a resource that is already being used: ' . $name);
    }

    $model = strtolower($model);

    if ($module == '') {
        $module = $ci->uri->router->fetch_module();
    } else {
        $module = 'modules/' . $module . '/';
    }

    if (!file_exists(APPPATH . $module . 'models/' . $path . $model . EXT)) {
        $module = '';
        if (!file_exists(APPPATH . 'models/' . $path . $model . EXT)) {
            show_error('Unable to locate the model you have specified: '.$model);
        }
    }

    if ($db_conn !== false && !class_exists('CI_DB')) {
        if ($db_conn === true) {
            $db_conn = '';
        }

        $ci->load->database($db_conn, false, true);
    }

    if (!class_exists('Model')) {
        require_once(BASEPATH . 'libraries/Model' . EXT);
    }

    require_once(APPPATH . $module . 'models/' . $path . $model . EXT);

    $model = ucfirst($model);

    $ci->$name = new $model();
    $ci->$name->_assign_libraries();

    $this->_ci_models[] = $name;    
}

function module_model($module, $model, $name = '', $db_conn = false)
{
    return $this->model($model, $name, $db_conn, $module);
}

function view($view, $vars = array(), $return = false, $module = '')
{
    return $this->_ci_load(array('view' => $view, 'vars' => $this->_ci_object_to_array($vars), 'return' => $return), $module);
}

function module_view($module, $view, $vars = array(), $return = false)
{
    return $this->view($view, $vars, $return, $module);
}

function _ci_load($data, $module = '')
{
    foreach (array('view', 'vars', 'path', 'return') as $val) {
        $$val = (!isset($data[$val])) ? false : $data[$val];
    }

    if ($path == '') {
        $ext = pathinfo($view, PATHINFO_EXTENSION);
        $file = ($ext == '') ? $view . EXT : $view;
        $path = $this->_ci_view_path . $file;

        if ($module == '') {
            $ci = &get;_instance();
            $module = $ci->uri->router->fetch_module();
        }else{
            $module = 'modules/' . $module . '/';
        }

        $orig = $path;
        $path = APPPATH . $module . 'views/' . $file;

        if (!file_exists($path)) {
            $path = $orig;
        }
    } else {
        $x = explode('/', $path);
        $file = end($x);
    }

    if (!file_exists($path)) {
            show_error('Unable to load the requested file: ' . $file);
    }

    if ($this->_ci_is_instance()) {
        $ci = &get;_instance();
        foreach (get_object_vars($ci) as $key => $var) {
            if (!isset($this->$key)) {
                $this->$key = &$ci->$key;
            }
        }
    }

    if (is_array($vars)) {
        $this->_ci_cached_vars = array_merge($this->_ci_cached_vars, $vars);
    }
    extract($this->_ci_cached_vars);

    ob_start();

    if ((bool) @ini_get('short_open_tag') === false && config_item('rewrite_short_tags') == true) {
        echo eval&#40;'?&gt;' . preg_replace("/;*\s*\?&gt;/", "; ?&gt;", str_replace('&lt;?=', '&lt;?php echo ', file_get_contents&#40;$path&#41;&#41;) . '&lt;?php ');
    } else {
        include($path);
    }

    log_message('debug', 'File loaded: ' . $path);

    if ($return === true) {
        $buffer = ob_get_contents();
        @ob_end_clean();
        return $buffer;
    }

    if (ob_get_level() > $this->_ci_ob_level + 1) {
        ob_end_flush();
    } else {
        global $OUT;
        $OUT->set_output(ob_get_contents());
        @ob_end_clean();
    }
}

} ?>[/code]

[b]application/libraries/MY_Router.php[/b] [code]<?php if (!defined('BASEPATH')) { exit('No direct script access allowed'); }

class MY_Router extends CI_Router { var $module = ''; var $found = false;

function _set_route_mapping()
{        
    if ($this->config->item('enable_query_strings') === true && isset($_GET[$this->config->item('controller_trigger')])) {
        $this->set_class($_GET[$this->config->item('controller_trigger')]);

        if (isset($_GET[$this->config->item('function_trigger')])) {
            $this->set_method($_GET[$this->config->item('function_trigger')]);
        }

        return;
    }

    @include(APPPATH . 'config/routes' . EXT);
    $this->routes = (!isset($route) || !is_array($route)) ? array() : $route;
    unset($route);

    $this->default_controller = (!isset($this->routes['default_controller']) || $this->routes['default_controller'] == '') ? false : strtolower($this->routes['default_controller']);    

    $this->uri_string = $this->_get_uri_string();

    if ($this->uri_string == '/') {
        $this->uri_string = '';
    }

    if ($this->uri_string == '') {
        if ($this->default_controller === false) {
            show_error("Unable to determine what should be displayed. A default route has not been specified in the routing file.");
        }

        if (is_dir(APPPATH . 'modules/' . $this->default_controller)) {
            $this->set_module($this->default_controller);
        }

        $this->set_class($this->default_controller);
        $this->set_method('index');

        log_message('debug', "No URI present. Default controller set.");
        return;
    }
    unset($this->routes['default_controller']);

    if ($this->config->item('url_suffix') != "") {
        $this->uri_string = preg_replace("|" . preg_quote($this->config->item('url_suffix')) . "$|", "", $this->uri_string);
    }

    foreach(explode("/", preg_replace("|/*(.+?)/*$|", "\\1", $this->uri_string)) as $val) {
        $val = trim($this->_filter_uri($val));

        if ($val != '') {
            $this->segments[] = $val;
        }
    }

    $this->_parse_routes();        

    $this->_reindex_segments();
}

function _validate_segments($segments)
{
    if (count($segments) > 1 && $segments[0] !== $segments[1] && file_exists(APPPATH . 'modules/' . $segments[0] . '/controllers/' . $segments[1] . EXT)) {
        $this->set_module($segments[0]);
        $this->set_directory('./');
        $segments = array_slice($segments, 1);

        return $segments;
    }

    if (file_exists(APPPATH . 'modules/' . $segments[0] . '/controllers/' . $segments[0] . EXT)) {
        $this->set_module($segments[0]);

        return $segments;
    }

    parent::_validate_segments($segments);
}

function fetch_directory()
{
    if (!$this->found) {
        $this->found = true;

        return '../' . $this->fetch_module() . 'controllers/' . $this->directory;
    }

    return $this->directory;
}

function set_module($module)
{
    $this->module = 'modules/' . $module . '/';
}

function fetch_module()
{
    return $this->module;
}

} ?>[/code]

[h3]How to use[/h3] A module consists of at least one controller and might include a number of models and/or views. These must be placed in the [b]application/modules/MODULENAME/[/b] folder (with [b]MODULENAME[/b] being the name of your module, ie. blog, forum, etc).

The files must be placed in the following folders:

[b]/system/application/modules/MODULENAME/models/ /system/application/modules/MODULENAME/views/ /system/application/modules/MODULENAME/controllers/[/b]

Your controllers will then be accessible as [b]www.example.com/MODULENAME/CONTROLLER/METHOD[/b].

Consider this URI:

[b]www.example.com/blog/entries/view/[/b]

In the above example, CI will open the [b]application/modules/blog/controllers/entries.php[/b] controller and call the [b]view[/b] method within it.

However, an URI like this:

[b]www.example.com/blog/[/b]

Will only look for the controller with the same name as the module, ie. [b]application/modules/blog/controllers/blog.php[/b] controller and call the [b]index[/b] method.

In order to let you call methods from your default controller (the one with the same name as your module, application/modules/blog/controllers/blog.php), CI will in fact handle this URI:

[b]www.example.com/blog/entries/view/[/b]

By first looking for a method called entries in [b]application/modules/blog/controllers/blog.php[/b] and then for a [b]application/modules/blog/controllers/entries.php[/b] and call its [b]view[/b] method.

You can even put controllers in sub-folders. If you do, the same rules apply as above, except the URI will need to have the FOLDERNAME before your MODULENAME:

[b]www.example.com/FOLDERNAME/MODULENAME/METHOD/[/b] (if default controllers has the called METHOD)

or

[b]www.example.com/FOLDERNAME/MODULENAME/CONTROLLER/METHOD/[/b]

[h3]Important note for PHP4 users[/h3] Because of a bug, if you are using PHP4, CI does not extend the loader class. Until Rick fixes this, you can do it yourself by editing the [b]Base4.php[/b] file like this:

[b]system/codeigniter/Base4.php[/b] [code]... class CI_Base extends MY_Loader { ...[/code] That is, replacing CI_Loader with MY_Loader.

Clone this wiki locally