26

I'm looking for a one line route to route dashed controller and method names to the actual underscored controller and method names.

For example the URL

/controller-name/method-name-which-is-long/

would route to

/controller_name/method_name_which_is_long/

see: http://codeigniter.com/forums/viewreply/696690/ which gave me the idea to ask :)

DisgruntledGoat
  • 70,219
  • 68
  • 205
  • 290

9 Answers9

48

That is exactly my requirement too and I was using routes like

$route['logued/presse-access'] = "logued/presse_access";

In my previous project I needed to create 300-400 routing rules, most of them are due to dash to underscore conversion.

For my next project I eagerly want to avoid it. I have done some quick hack and tested it, though have not used in any live server, its working for me. Do the following..

Make sure the subclass_prefix is as follows in your system/application/config/config.php

$config['subclass_prefix'] = 'MY_';

Then upload a file named MY_Router.php in system/application/libraries directory.

<?php

class MY_Router extends CI_Router { 
    function set_class($class) 
    {
        //$this->class = $class;
        $this->class = str_replace('-', '_', $class);
        //echo 'class:'.$this->class;
    }

    function set_method($method) 
    {
//      $this->method = $method;
        $this->method = str_replace('-', '_', $method);
    }

    function _validate_request($segments)
    {
        // Does the requested controller exist in the root folder?
        if (file_exists(APPPATH.'controllers/'.str_replace('-', '_', $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);

            if (count($segments) > 0)
            {
                // Does the requested controller exist in the sub-folder?
                if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().str_replace('-', '_', $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]);
    }
}

Now you can freely use url like http://example.com/logued/presse-access and it will call the proper controller and function by automatically converting dash to underscore.

Edit: Here is our Codeigniter 2 solution which overrides the new CI_Router functions:

<?php

class MY_Router extends CI_Router { 
    function set_class($class) 
    {
        $this->class = str_replace('-', '_', $class);
    }

    function set_method($method) 
    {
        $this->method = str_replace('-', '_', $method);
    }

    function set_directory($dir) {
        $this->directory = $dir.'/';
    }

    function _validate_request($segments)
    {
        if (count($segments) == 0)
        {
            return $segments;
        }

        // Does the requested controller exist in the root folder?
        if (file_exists(APPPATH.'controllers/'.str_replace('-', '_', $segments[0]).'.php'))
        {
            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);


            while(count($segments) > 0 && is_dir(APPPATH.'controllers/'.$this->directory.$segments[0]))
            {
                // Set the directory and remove it from the segment array
                $this->set_directory($this->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().str_replace('-', '_', $segments[0]).'.php'))
                {
                    if ( ! empty($this->routes['404_override']))
                    {
                        $x = explode('/', $this->routes['404_override']);

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

                        return $x;
                    }
                    else
                    {
                        show_404($this->fetch_directory().$segments[0]);
                    }
                }
            }
            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.'.php'))
                {
                    $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]);
    }
}

Now one has to place this file like application/core/MY_Router.php and make sure he has subclass_prefix is defined as $config['subclass_prefix'] = 'MY_'; in application/config/config.php

Few extra lines of code has been added in method _validate_request():

while(count($segments) > 0 && is_dir(APPPATH.'controllers/'.$this->directory.$segments[0]))
{
    // Set the directory and remove it from the segment array
    $this->set_directory($this->directory . $segments[0]);
    $segments = array_slice($segments, 1);
}

It is used so that one can make use of multi-level subdirectory inside controllers directory, whereas normally we can use single level subdirectory inside controllers folder and can call it in url. One can remove this code if it not necessary but it has no harm on the normal flow.

sumanchalki
  • 1,457
  • 17
  • 21
  • 3
    +1 that's the solution, it is almost the same way used to get command line arguments properly routed to controller methods – Benoit Mar 12 '10 at 12:28
  • I tried following this solution with the 2.0 build to no avail - can anyone provide any insight? – Dave Kiss Nov 27 '10 at 21:48
  • 1
    FYI, found the solution using hooks here - http://codeigniter.com/forums/viewthread/124396/#644012 – Dave Kiss Nov 28 '10 at 03:44
  • Dave, it is hack to the router when calculating the controller name and method name; it is replacing the dash in controller name and method name so that one can use dash in url, though original underscored url will also work, and the query string parameters in url remain unchanged. My 2 live projects are running properly using it. – sumanchalki Nov 30 '10 at 12:36
  • 3
    Using Codeigniter 2 I had to put the file in `system/application/core/MY_Router.php` instead of `system/application/libraries/MY_Router.php` for CI to load the file. Hope this is to help to anyone looking to do the same thing. – Hultner Mar 14 '11 at 12:56
  • Hey Hultner thank you very, very much. I was going crazy :) and such a simple step to do... Ain't it always like that :D – Ivan Ivanic Jun 28 '11 at 22:02
  • Does this solution works in Codeigniter 3? For me this does not help a lot. – Ashis Jan 27 '15 at 11:32
  • @Ashis "$route['translate_uri_dashes'] = TRUE;" in routes.php – kanarifugl Jan 14 '16 at 15:08
20

Just coming back to this question after upgrading to CodeIgniter 2. Here is a solution which is more robust than the accepted answer because it will survive CodeIgniter core updates.

<?php
class MY_Router extends CI_Router
{ 
    public function set_class($class) 
    {
        parent::set_class($this->_repl($class));
    }

    public function set_method($method) 
    {
        parent::set_method($this->_repl($method));
    }

    public function _validate_request($segments)
    {
        if (isset($segments[0]))
            $segments[0] = $this->_repl($segments[0]);
        if (isset($segments[1]))
            $segments[1] = $this->_repl($segments[1]);

        return parent::_validate_request($segments);
    }

    private function _repl($s)
    {
        return str_replace('-', '_', $s);
    }
}

It should be saved as application/core/MY_Router.php. Now, you can have Controller and Method names with underscores in them such as Abc_Def (in file abc_def.php) and refer to them with the URL /abc-def.

DisgruntledGoat
  • 70,219
  • 68
  • 205
  • 290
  • Took me two seconds to add this and it works perfectly. Thanks! – Peter Aug 08 '13 at 02:42
  • Simple and nice. But can not able to make it work for Codeigniter 3. This router seems to be not at all called on a shared hosting server. – Ashis Jan 27 '15 at 11:53
  • @Ashis according to the [CI3 docs](http://www.codeigniter.com/userguide3/general/core_classes.html), extending core classes works the same as it did before. You might want to look at the original CI_Router class to see if it's still compatible with the override `MY_Router` one. – DisgruntledGoat Jan 27 '15 at 11:57
9

Actually this is native now in Codeigniter 3 Just do this in routes file

   $route['translate_uri_dashes'] = TRUE;

And it will do it for controllers and methods automatically .
Please vote this answer up , as it's most recent

Samy Massoud
  • 4,295
  • 2
  • 35
  • 48
3
<?php
class MY_Router extends CI_Router
{
 function _set_request($segments = array()) {
  parent::_set_request(str_replace('-', '_', $segments));
 }
}
?>

Put this file MY_Router.php inside /application/libraries (CI1) or /application/core (CI2) Remember that this will effect all segments, not only module, controller and method.

Alternative to this extend is to add each segment to router.php $route['this-is-a-module-or-controler'] = 'this_is_a_module_or_controller';

As you can see the extend method would be easier to use. You can choose to make the function also to handle only the first two or three segments so that the other segments are not affected with the _ replacement.

e-mike
  • 31
  • 1
  • I tried this and it's OK for a quick fix but to clarify - it *changes all segments to use underscore* instead of dashes. So `/my-controller` will go to `my_controller` but also `ctrl/func/my-data` will convert the data to `my_data`, which may break many of your pages (it did with mine). The accepted answer is much more robust. – DisgruntledGoat Nov 21 '10 at 23:58
  • This answer is actually a lot more simpler, and it's easily configured to only alter the first 2 items in the segments array. `for($i = 0; $i < 2; ++$i){ if(isset($segments[$i])){ $segments[$i] = str_replace('-', '_', $segments[$i]); } } parent::_set_request($segments);` – Jason Lewis May 08 '11 at 10:20
2

This is an old question, but I'd like to post that e-mike had a great solution to this problem, and a lot simpler.

<?php
    public function _set_request($segments){
        // Fix only the first 2 segments
        for($i = 0; $i < 2; ++$i){
            if(isset($segments[$i])){
                $segments[$i] = str_replace('-', '_', $segments[$i]);
            }
        }

        // Run the original _set_request method now, giving it our newly replaced segments
        parent::_set_request($segments);
    }
?>

Hope this helps anyone else with this problem.

Jason Lewis
  • 18,537
  • 4
  • 61
  • 64
  • But it will not work if Controllers are placed in a sub-directory inside controllers directory, like inside controllers/admin/ as the first 2 segments will become admin and controller-name then. – sumanchalki Dec 01 '11 at 11:15
1

I believe what you are looking for is a either a pre-system or else a pre-controller hook that will take the requested URI and update it.

Sean Vieira
  • 155,703
  • 32
  • 311
  • 293
1

Overriding the Router class is a nice approach, there is also a way of replacing - with _ by registering a "pre-system" hook.

First create the hook by adding these lines to your ‘config/hooks.php’ file:

$hook['pre_system'] = array(
    'class'    => '',
    'function' => 'prettyurls',
    'filename' => 'myhooks.php',
    'filepath' => 'hooks',
    'params'   => array()
); 

Now create a ‘myhooks.php’ file within the ‘application/hooks’ folder and add this function (don’t forget to open a PHP tag if this is the first hook):

<?php
function prettyurls() {
    if (is_array($_GET) && count($_GET) == 1 && trim(key($_GET), '/') != '') {
        $newkey = str_replace('-','_',key($_GET));
        $_GET[$newkey] = $_GET[key($_GET)];
        unset($_GET[key($_GET)]);
    }
    if (isset($_SERVER['PATH_INFO'])) $_SERVER['PATH_INFO'] = str_replace('-','_',$_SERVER['PATH_INFO']);
    if (isset($_SERVER['QUERY_STRING'])) $_SERVER['QUERY_STRING'] = str_replace('-','_',$_SERVER['QUERY_STRING']);
    if (isset($_SERVER['ORIG_PATH_INFO'])) $_SERVER['ORIG_PATH_INFO'] = str_replace('-','_',$_SERVER['ORIG_PATH_INFO']);
    if (isset($_SERVER['REQUEST_URI'])) $_SERVER['REQUEST_URI'] = str_replace('-','_',$_SERVER['REQUEST_URI']);
} 

You will probably need to edit your ‘config/config.php’ file to enable hooks (around line 91 for me):

$config['enable_hooks'] = TRUE; 

This answer is ripped from http://codeigniter.com/forums/viewthread/124396/#644012

André Laszlo
  • 15,169
  • 3
  • 63
  • 81
0

I'm not sure if you could do that with a route...

However, somewhere in the Codeigniter core libraries (possibly Router or URI) will be something that converts the underscored uris into a camelcase class name.

I had a quick look and couldn't find it, but if you do, just copy that library to your application/libraries folder, and modify it there.

Josh
  • 1,261
  • 2
  • 18
  • 34
0

You can use this _remap() method to handle such behavior. Place this method in your controllers or create a core controller and place it in.

public function _remap($method, $params = array()){
    if(method_exists($this, $method)){
        return call_user_func_array(array($this, $method), $params);
    }else{
        $method = str_replace("-", "_", $method);
        if(method_exists($this, $method)){
            return call_user_func_array(array($this, $method), $params);
        }
    }
    show_404();
}
Sajjad Ashraf
  • 3,754
  • 1
  • 34
  • 35