Welcome Guest, Not a member yet? Register   Sign In
Multi-level subfolders for controllers in CI 2.x
#1

[eluser]sehummel[/eluser]
I've seen several posts on here that talk about how to handle multi-level subfolders in CI 2.x but no one has posted the complete code. I found this post: http://ellislab.com/forums/viewthread/129469/, but the complete solution was not posted. Could someone please post the entire script, with detailed instructions on how to implement. I couldn't sort out how to make the changes from the 1.7.1 code.

Thanks.
Scott
#2

[eluser]Damien K.[/eluser]
Well, here you go sehummel: yet another multi-level subfolders controller extension. This was for 2.0.0. No changes in the affected methods in 2.0.2, so should work there as well.

This will support things like the following:
(Note that 'default_controller' is as defined in 'config/routers.php')

If you have the following controllers:
controllers/user/<default_controller>.php
controllers/user/profile.php

Then:
http://www.myhost.com/user/ maps to http://www.myhost.com/user/<default_controller>
http://www.myhost.com/user/prefs maps to http://www.myhost.com/user/<default_controller>/prefs
http://www.myhost.com/user/profile maps to http://www.myhost.com/user/profile

I think there is a "bug" in CI 2+ where 'show_404()' will error because all the core classes are not loaded yet, hence it was not use in this code.

HTH, Damien K.

Code:
&lt;?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
* MY_Router Class
*
* @author        Damien K.
*/
class MY_Router extends CI_Router
{
    // --------------------------------------------------------------------

    /**
     * OVERRIDE
     *
     * Validates the supplied segments.  Attempts to determine the path to
     * the controller.
     *
     * @access    private
     * @param    array
     * @return    array
     */
    function _validate_request($segments)
    {
        if (count($segments) == 0)
        {
            return $segments;
        }

        // Does the requested controller exist in the root folder?
        if (file_exists(APPPATH.'controllers/'.$segments[0].EXT))
        {
            return $segments;
        }

        // Is the controller in a sub-folder?
        if (is_dir(APPPATH.'controllers/'.$segments[0]))
        {
            // @edit: Support multi-level sub-folders
            $dir = '';
            do
            {
                if (strlen($dir) > 0)
                {
                    $dir .= '/';
                }
                $dir .= $segments[0];
                $segments = array_slice($segments, 1);
            } while (count($segments) > 0 && is_dir(APPPATH.'controllers/'.$dir.'/'.$segments[0]));
            // Set the directory and remove it from the segment array
            $this->set_directory($dir);
            // @edit: END

            // @edit: If no controller found, use 'default_controller' as defined in 'config/routes.php'
            if (count($segments) > 0 && ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().$segments[0].EXT))
            {
                array_unshift($segments, $this->default_controller);
            }
            // @edit: END

            if (count($segments) > 0)
            {
                // Does the requested controller exist in the sub-folder?
                if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().$segments[0].EXT))
                {
                    // show_404($this->fetch_directory().$segments[0]);
                    // @edit: Fix a "bug" where show_404 is called before all the core classes are loaded
                    $this->directory = '';
                    // @edit: END
                }
            }
            else
            {
                // Is the method being specified in the route?
                if (strpos($this->default_controller, '/') !== FALSE)
                {
                    $x = explode('/', $this->default_controller);

                    $this->set_class($x[0]);
                    $this->set_method($x[1]);
                }
                else
                {
                    $this->set_class($this->default_controller);
                    $this->set_method('index');
                }

                // Does the default controller exist in the sub-folder?
                if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().$this->default_controller.EXT))
                {
                    $this->directory = '';
                    return array();
                }

            }

            return $segments;
        }


        // If we've gotten this far it means that the URI does not correlate to a valid
        // controller class.  We will now see if there is an override
        if (!empty($this->routes['404_override']))
        {
            $x = explode('/', $this->routes['404_override']);

            $this->set_class($x[0]);
            $this->set_method(isset($x[1]) ? $x[1] : 'index');

            return $x;
        }


        // Nothing else to do at this point but show a 404
        show_404($segments[0]);
    }

    // --------------------------------------------------------------------

    /**
     * OVERRIDE
     *
     * Set the directory name
     *
     * @access    public
     * @param    string
     * @return    void
     */
    function set_directory($dir)
    {
        $this->directory = str_replace(array('.'), '', $dir).'/'; // @edit: Preserve '/'
    }

}
#3

[eluser]sehummel[/eluser]
This is great, Damien, but I can't seem to get it working. I placed the code in a file called MY_router.php in the application/libraries folder and uploaded it. I have a controller called markets that I put in the test folder in the cms folder (controllers/cms/test/markets.php). I uploaded this folder structure and even built a route for it. But I get a 404 error.

My subclass prefix is set to the default of "MY_" so that's not the problem. Any idea off hand what I might be doing wrong? Will your router file work for the folder structure I have? It looks like it from your post, but I wasn't quite sure.

I really appreciate your help with this!
#4

[eluser]cideveloper[/eluser]
MY_router.php should be put into application/core folder
#5

[eluser]gbar[/eluser]
this solution was useful like for me, I tried for days ...
thanks
#6

[eluser]gbar[/eluser]
How could I extend your solution also to the models folder and views folder?

Thanks
#7

[eluser]Damien K.[/eluser]
@sehummel Like what cideveloper said.
#8

[eluser]Damien K.[/eluser]
@gbar I think multi-level folders/directories already works for models and views.

$this->load->model('shopping/international/exchange_model');
$this->load->view('shopping/cart/favourite');
#9

[eluser]gbar[/eluser]
I worked on this thing all day yesterday, making a lot of testing ...
is true, a structure so it works:

Code:
$this->load->model(‘shopping/international/exchange_model’);
$this->load->view(‘shopping/cart/favourite’);

but it is painfully slow, at least in my experience I had ...
This morning I reported levels of depth views and models in a single folder

Code:
$this->load->model(‘folder/model’);
Code:
$this->load->view(‘folder/form_view’);

and the application has reacquired the optimal speed, while your solution for controllers, it works really well.
#10

[eluser]jjDeveloper[/eluser]
I came up with something nifty that works for me, feel free to try it out.

In your applications core library if you don't already have one add a subclass of CI_Loader and override the view function like so.

Code:
class MY_Loader extends CI_Loader {


    function view($view, $vars = array(), $return = FALSE)
     {
       $seg = explode('<>', $view);
       $out = '';
       $ct = 0;
       If($seg != FALSE) {
         foreach($seg as $dir) {
           switch($dir) {
            case 'f':
              $out .= 'forms/';
              break;
            case 't':
              $out .= 'template/';
              break;
            case 'r':
              $out .= 'registration/';
              break;
           }
           $ct++;
         }
         $view = $out . $seg[($ct - 1)];
       }
             return $this->_ci_load(array('_ci_view' => $view, '_ci_vars' => $this->_ci_object_to_array($vars), '_ci_return' => $return));
     }
}

To make this function work for your needs however you will have to add case statements for the sub-folders that you wish to abbreviate so to speak.

Lets say in your views you have a folder named content you would just add the following code to the switch statement.

Code:
case 'c':
              $out .= 'content/';
              break;

The resulting view calls to this folder can now be done using the following load call

Code:
$this->load->view('c<>some_content');

This will also work if you have multi-level sub-folders like this

Code:
$this->load->view('f<>r<>some_view');

This would have the same effect as doing this.

Code:
$this->load->view('forms/registration/some_view');


I find it helps code readability and also results in less typing when you wish to load views out of sub-folders. Keep in mind this addition will still retain the normal functionality if you load the view with its full path as well.

I just thought this might be something you may want to look at. I enjoy using it so hope someone else does too.

Cheers




Theme © iAndrew 2016 - Forum software by © MyBB