Welcome Guest, Not a member yet? Register   Sign In
Are Multiple Sub-folders for Controllers Allowed?
#1

[eluser]Matt Johnson[/eluser]
Hi All,

I'm in the midst of developing a CI website/app for a client and I've run into a little snag. The admin panel for this website is a bit large due to the vast amount of information the client wants to be able to input for the website.

Right now I have a subfolder in controllers called admin where the admin controllers reside. So for instance, I have:

controllers/admin/blog.php (Controller to manage the blog)
controllers/admin/users.php (Controller to manage the users...)

and so on.

I would like to, if possible, create another sub-folder in the admin folder to organize additional and much larger sections of the admin section. Like so:

controllers/admin/section1/default.php (this would be section1's default controller)

This would allow me to NOT have a section1 controller in admin that has way too many functions in it.

However it appears that CI is unable of supporting two levels deep (or more) of subfolders for controllers. Is this true or am I just missing something?

If I try to access that section by going to the URL like index.php/admin/section1/ I just get a 404 error even with $route['admin/section1'] = 'admin/section1/default'; set in the routes config file.

Thanks for any suggestions/ways around this you may have! Thanks
#2

[eluser]Matt Johnson[/eluser]
No suggestions? Any would be appreciated. Thanks
#3

[eluser]Rick Jolly[/eluser]
Yes, you are correct that CI only allows controllers one subfolder deep. You can use routes to have any url structure you like. If you want to add more organization to your files, have a look at Matchbox or Modular Extensions.
#4

[eluser]Matt Johnson[/eluser]
Thank you very much. I'll look into those.
#5

[eluser]hvalente13[/eluser]
Quote:CI only allows controllers one subfolder deep.

I've managed to get more than one subfolder level like this:
Code:
application
   |
   +---- controllers
            |
            +---- folder1
                     |
                     +---- subfolder1

And then you can link to the controllers in subfolder1 by

Code:
base_url().'index.php/folder1/subfolder1/controller_app/function'
#6

[eluser]Matt Johnson[/eluser]
[quote author="hvalente13" date="1216707699"]
Quote:CI only allows controllers one subfolder deep.

I've managed to get more than one subfolder level like this:
Code:
application
   |
   +---- controllers
            |
            +---- folder1
                     |
                     +---- subfolder1

And then you can link to the controllers in subfolder1 by

Code:
base_url().'index.php/folder1/subfolder1/controller_app/function'
[/quote]

I'm not sure how that's working for you as that's what I've basically tried to do.

In any event, I ended up creating my own Router class (copy and pasted the default Router class).

I then modified the function/method _validate_request() to be able to handle multiple sub-folders. By default, obviously, the system only looks to see if the first segment (segment[0]) is the directory we want. However, I just wrote some code in a loop to see what the deepest directory structure we could make.

In other words, if the segment is folder1/subfolder1/controller then it'll check to see if folder1 is a directory, then folder1/subfolder1 and then finally fail on folder1/subfolder1/controller since it isn't a directory, thereby making folder1/subfolder1 the longest folder. Then it uses this as the folder and clears the segment array of the directory related entries (in this case 'folder1' and 'subfolder1').

Here's the code I wrote. I suspect there are better/more efficient solutions but this is what I came up with in 10 minutes in case anyone is looking for a solution. (I add the code between NEW CODE and END NEW CODE and also comment out the two lines before it)

Code:
function _validate_request($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]))
        {        
            // Set the directory and remove it from the segment array
            //$this->set_directory($segments[0]);
            //$segments = array_slice($segments, 1);
            
            /* -- NEW CODE -- */
            $tempDir = array();
            $i = 0;

            for(; $i < count($segments); $i++)
            {
                // We keep going until we can't find a directory
                $tempDir[] = $segments[$i];
                if(!is_dir(APPPATH.'/controllers/'.implode('/', $tempDir)))
                {
                    // The last "segment" is not a part of the "directory" so we can get rid of it.
                    unset($tempDir[count($tempDir)-1]);
                    break;
                }
            }

            $this->set_directory(implode('/', $tempDir));
            $segments = array_slice($segments, $i);
            /* -- END NEW CODE -- */
            
            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]);
                }
            }
            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;
        }

        // Can't find the requested controller...
        show_404($segments[0]);
    }

Please let me know if you find any errors/problems. I suppose this should work with any number of sub-directories.
#7

[eluser]andyr[/eluser]
I have sub-classed the router, to allow a flexible nested controller structure, which allows a controller to be binded to a relative directory.

In simple terms, if you have a controller that has many reponsibilities, you may require additional controllers to breakdown those reponsibilties. The binding allows a controller (SubController1) to be used, with a binding to a directory (SubController1) where additional related controllers may be placed.

This gives the benefit of an unlimited nested controller structure while allowing the controller to keep its simplicity for a more friendly URI.

For example,

Code:
Application
--------------------+ Order
----------------------------------------+ Manage
--------------------------------------------------+ Equipment.php
------------------------------+ Manage.php
------------------------------+ List.php
--------------------+ Order.php

Routing Example
---------------

Application/Order > Application/Order.php
Application/Order/Manage > Application/Order/Manage.php
Application/Order/Manage/Equipment > Application/Order/Manage/Equipment.php

Code
---------------

Code:
&lt;?php
if (! defined ( 'BASEPATH' ))
    exit ( 'No direct script access allowed' );

class MY_Router extends CI_Router {
    
    var $_route = array ();
    
    function MY_Router() {
        parent::CI_Router ();
    }
    
    function _set_routing() {
        parent::_set_routing ();
        
        // re-routed url
        if ($this->uri->rsegments != $this->segments) {
            if (count ( $this->uri->rsegments ) > 0) {
                array_unshift ( $this->uri->rsegments, $this->_route ['directory'] );
            }
        }
    }
    
    function _build_sub_route($segments) {
        
        $route = array ();
        
        // Recurse through the sub directory route finding the directory path, controller and method
        $this->_recurse_sub_route ( $segments );
        
        // Build the route to the directory, controller, method with parameters
        $route [0] = empty ( $this->_route ['directory'] ) ? null : implode ( $this->_route ['directory'], DIRECTORY_SEPARATOR );
        $route = @array_merge ( $route, $this->_route ['controller'] );
        $route = @array_merge ( $route, $this->_route ['parameter'] );
        
        $this->segments = $route;
        
        return $route;
    }
    
    function _recurse_sub_route($segments) {
        
        // Find the all directories and files to be routed
        foreach ( $segments as $k => $segment ) {
            
            $directory = @implode ( $this->_route ['directory'], DIRECTORY_SEPARATOR );
            
            // Find all directories
            if (is_dir ( APPPATH . 'controllers/' . $directory . DIRECTORY_SEPARATOR . $segment )) {
                $this->_route ['directory'] [$k] = $segment;
            }
            
            // Find all controllers
            if (is_file ( APPPATH . 'controllers/' . $directory . DIRECTORY_SEPARATOR . $segment . EXT )) {
                $this->_route ['controller'] [$k] = $segment;
            }
        
        }
        
        // Find the controller in route
        $controller = @array_slice ( $this->_route ['controller'], - 1, 1, true );
        // Determine the parameters after controller
        $this->_route ['parameter'] = @array_slice ( $segments, key ( $controller ) + 1 );
        
        // Remove controller binding from directory route
        if (@array_key_exists ( key ( $controller ), $this->_route ['directory'] )) {
            unset ( $this->_route ['directory'] [key ( $controller )] );
        }
        
        // Remove any directories from controller route
        if (! empty ( $this->_route ['directory'] )) {
            $this->_route ['controller'] = @array_diff ( $this->_route ['controller'], $this->_route ['directory'] );
        }
    }
    
    function _validate_request($segments) {
        
        $segments = $this->_build_sub_route ( $segments );
        
        // Is the controller in a sub-folder?
        if (is_dir ( APPPATH . 'controllers/' . $segments [0] )) {
            
            // Set the directory and remove it from the segment array
            $this->set_directory ( $segments [0] );
            $segments = @array_slice ( $segments, 1 );
            
            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 ();
                }
            } 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;
        }
        
        // Can't find the requested controller...
        show_404 ();
    }
}

?&gt;
#8

[eluser]DavidZB[/eluser]
I found a way to allow more than one or two subfolders in the controllers folder. It was easy, and allows all of the normal functionality, even the method's params.... But i had to doit on the very same Router.php library.... anyway, if you want, is better to save a copy from your original Router.php class before testing this code.... here it goes:

In CodeIgniter 1.7.0, in the Router.php library, in the
Code:
_validate_request($segments)
function around line 205-207 there's the next code:

Code:
// Set the directory and remove it from the segment array
$this->set_directory($segments[0]);
$segments = array_slice($segments, 1);

Just after that code, insert the next lines:

Code:
//----------- NEW CODE ------------

while(is_dir(APPPATH.'controllers/'.$this->directory.$segments[0]) && count($segments) > 0){

    // Set the directory and remove it from the segment array
    $this->set_directory($this->directory . $segments[0]);
    $segments = array_slice($segments, 1);
}

//----------- END OF NEW CODE ------------

so at the end, it should look like this:

Code:
// Set the directory and remove it from the segment array
$this->set_directory($segments[0]);
$segments = array_slice($segments, 1);

//----------- NEW CODE ------------

while(is_dir(APPPATH.'controllers/'.$this->directory.$segments[0]) && count($segments) > 0){

    // Set the directory and remove it from the segment array
    $this->set_directory($this->directory . $segments[0]);
    $segments = array_slice($segments, 1);
}

//----------- END OF NEW CODE ------------

After this, everything is just normal. Save and test. You'll find this very usefull (it was for me). I hope this functionality to be inside the next CodeIgniter's version, jeje.

PD1: Sorry about my english. It isn't my natal language.
PD2: If this thread is already closed, it doesn't matter. I found this thread; someone else will find it too.
#9

[eluser]Unknown[/eluser]
[quote author="DavidZB" date="1232193221"]
Code:
while(is_dir(APPPATH.'controllers/'.$this->directory.$segments[0]) && count($segments) > 0){
[/quote]

Do the following to avoid the Undefined Offset error.

Code:
while(count($segments) > 0 && is_dir(APPPATH.'controllers/'.$this->directory.$segments[0]))

Thanks for the fix. Hopefully it makes it into the main code tree at some point.
#10

[eluser]searain[/eluser]
CI 2.0

Router
set_directory changed to
Code:
function set_directory($dir)
    {
        $this->directory = str_replace(array('/', '.'), '', $dir).'/';
    }

So in your codes, set_directory won't work due to the / would be replaced.

We would need to override the set_directory back
Code:
function set_directory($dir)
    {
        $this->directory = $dir.'/';
    }

But would it affect other codes if we do so?




Theme © iAndrew 2016 - Forum software by © MyBB