Welcome Guest, Not a member yet? Register   Sign In
[Helper] Automatically link to routes in site_url(), redirect() and anchor()
#1

[eluser]Phil Sturgeon[/eluser]
Setting up routes is all well and good but when you change the route you need to run around your application changing all links to it.

I made a small modification that will allow you to link to the normal URI then have it output the route in the URL.

An example of supported routes:

Code:
$route['edit-settings'] = "users/settings/edit";
$route['page-:any-:any'] = 'pages/$2/$1';

You can then use:

Code:
<?=anchor('users/settings/edit', 'Edit Setting');?>

and it will link you to http://example.com/edit-settings.html.

Here is the code in the form of an extended MY_url_helper.php.

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

/**
* CodeIgniter URL Helpers - Extended
*
* @package        CodeIgniter
* @subpackage    Helpers
* @category    Helpers
* @author        Philip Sturgeon
*/

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

/**
* Site URL
*
* Create a local URL based on your basepath. Segments can be passed via the
* first parameter either as a string or an array.
*
* @access    public
* @param    string
* @return    string
*/
if ( ! function_exists('site_url'))
{
    function site_url($uri = '')
    {
        include(APPPATH.'config/routes.php');
        
        // Is there a literal match?  If so we're done
        if (in_array($uri, $route))
        {
            $uri = array_search($uri, $route);
        }
        
        else
        {
            foreach ($route as $key => $val)
            {                        
                if(!$key or !$val) continue;
                
                preg_match_all('#(:any|:num)+#', $key, $rules);
                preg_match_all('#\$([0-9]+)#', $val, $references);
                
                if(empty($rules[0]) or empty($references[0])) continue;
                
                for($i = 0; $i < count($rules[0]); $i++)
                {
                    $key = substr_replace($key, $references[0][$i], strpos($key, $rules[0][$i]), 4);
                    $val = substr_replace($val, $rules[0][$i], strpos($val, $references[0][$i]), 2);
                }
                
                $val = str_replace(':any', '(.+)', str_replace(':num', '([0-9]+)', $val));
                
                // Does the RegEx match?
                if (preg_match('#^'.$val.'$#', $uri))
                {
                    $uri = preg_replace('#^'.$val.'$#', $key, $uri);
                }
            }
        }

        $CI =& get_instance();
        return $CI->config->site_url($uri);
    }
}

/* End of file url_helper.php */
/* Location: ./system/helpers/url_helper.php */
?&gt;


Note: This only works with static routes and routes using :any or :num. That means if you use regex in your route it WILL NOT WORK. This is something I will look into, but not right now.
#2

[eluser]Phil Sturgeon[/eluser]
Should this be a URL helper or an extension to the Config class which is where site_url() functionality is actually kept?

I don't much like the idea of extending such a core class just to achieve something so tiny, but it would mean the site_url acts the same over the whole site and not just via the helpers.
#3

[eluser]Phil Sturgeon[/eluser]
Update! Got :any and :num routes working too. Now its just regex routes that don't work.
#4

[eluser]Phil Sturgeon[/eluser]
I guess I could look for anything thats not just text thats wrapped in ( ) ?

Can anyone think of a better way as my regex is not too hot.
#5

[eluser]zentair[/eluser]
I've made some improvements and modifications to this function:
1) Get the routes via $CI->router->routes
2) Match everything between parentheses
3) used preg_split and simply swap the even numbered matches
4) break after finding a match, this is required because in the Router higher routes will always take precedence over lower ones (http://ellislab.com/codeigniter/user-gui...uting.html).
5) I also added the functionality in http://ellislab.com/forums/viewthread/167695/

This works fine for routes like:
Code:
$route['view/(:any)/(:num)'] = 'entries/view/$1/$2';  
$route['admin/auth(.*)'] = 'auth$1';

It does not work for routes that use expressions without parentheses
Code:
$route['product/:num'] = "catalog/product_lookup";
fortunately I've never wanted to use such a route.

Code:
function site_url($uri = '')
{
    $CI = get_instance();
    if     (func_num_args() > 1) $uri = implode('/', func_get_args());
    elseif (is_array($uri))      $uri = implode('/', $uri);
    
    if (in_array($uri, $CI->router->routes))
    {
       $uri = array_search($uri, $CI->router->routes);
    }    
    else foreach ($CI->router->routes as $route => $replace)
    {
        $route   = preg_split('/(\(.+?\))/', $route,-1,PREG_SPLIT_DELIM_CAPTURE);
        $replace = preg_split('/(\$\d+)/', $replace,-1,PREG_SPLIT_DELIM_CAPTURE);
        if (count($route) != count($replace)) continue;

        $newroute = $newreplace = '';
        for ($i=0; $i < count($route); $i++)
            if ($i % 2)
            {
                $newroute .= $replace[$i];
                $newreplace .= $route[$i];
            }
            else
            {
                $newroute .= $route[$i];
                $newreplace .= $replace[$i];
            }
        $newreplace = str_replace(':any', '.+', str_replace(':num', '[0-9]+', $newreplace));
        
        if (preg_match("#^$newreplace\$#", $uri))
        {
            $uri = preg_replace("#^$newreplace\$#", $newroute, $uri);
            break;
        }
    }
    return $CI->config->site_url($uri);
}

Update: After discovering my site_url() calls were rewritten but form_open() wasn't I think it's better to add this functionality to the config class after all, config->site_url() is used by more helpers and libraries.
#6

[eluser]Phil Sturgeon[/eluser]
Awesome! I forgot about this thread entirely. I can't believe I publically admitted "My regex is not so hot". Good thing it was 2 years ago and now I am basically a regex god!

Would you be interested in sending this implementation (made to Config class as you suggest) in as a pull request on GitHub? I would be very happy to see this feature make it into a coming version.

For now you can use my GitHub account, as the BitBucket mirror is having a few issues.

https://github.com/philsturgeon/codeigniter-reactor
#7

[eluser]zentair[/eluser]
Sure thing, though I am new to GitHub so please tell me if I did something wrong: https://github.com/philsturgeon/codeigni...or/pull/36

I've been thinking about not being able to match expressions in routes without parentheses and apart from being completely unable to think of an example where that would be useful its also kind of strange. For example:
Code:
// with a route
$route['product/:num'] = "catalog/product_lookup";

// What should $url be?
$url = site_url('catalog/product_lookup);

There are an infinite number of 'right' awnsers for what $url could be.




Theme © iAndrew 2016 - Forum software by © MyBB