• 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Alternate URI syntax

#1
[eluser]TheFuzzy0ne[/eluser]
EDIT: These functions have been integrated with the URI class, as it allows you to write less code, and makes using the functions easier. Please see http://ellislab.com/forums/viewthread/106502/#536309 for the library.

Hi all. I just thought I'd share these two helper functions in case someone else found them useful.

I was concerned about using key-value paired segments, as all it takes is for one segment to be remove, and everything goes kaput. I was toying with the idea of using a similar format to query strings, but the = sign is disallowed in CodeIgniter by default.

The functions below allow you to use a URL with a syntax like this:

http://www.mysite.com/news/filter/catego...deigniter/

This implementation allows a more loose approach when it comes to ordering your URI segments. Obviously it's not useful if your segments are structured, but it's ideal for times when you need a little more flexibility.

Here are the functions:
Code:
<?php

/**
* This function accepts an associative array, and returns a string containing the
* key-value pairs within segments.
*
* The optional second parameter will prefix the the specified string to the
* beginning or the return string.
* The optional third parameter allows you to disable urlencode().
*
* @access public
* @param array
* @param bool
* @param bool
* @return string
*/
function assoc_to_alt_uri($input=array(), $prefix=FALSE, $encode_url=TRUE)
{
    $arr = array();
    $str = "";
    if (is_array($input) && count($input) > 0)
    {
        foreach ($input as $key => $val)
        {
            $str .= "/$key:" . ($encode_url == TRUE ? urlencode($val) : $val);
        }
    }
        
    if ($prefix)
    {
        $str = $prefix . $str;
    }
    else
    {
        $str = ltrim($str, '/');
    }
        
    $str = preg_replace('/[\/]+/', '/', $str);    
        
    return $str;        
}

/**
* This function operates directly on the URI, and returns an associative array.
*
* The optional parameter allows you to disable url decoding.
*
* @access public
* @param bool
* @return array
*/
function alt_uri_to_assoc($decode_url=TRUE)
{
    $arr = array();
    $segments = explode('/', $this->uri->uri_string());
    
    if (is_array($segments) && count($segments > 0))
    {
        foreach ($segments as $segment)
        {
            $segment = explode(':', $segment, 2);
            if (count($segment) == 2 && preg_match('/[a-z0-9\-_]+/i', $segment[0]))
            {
                $arr[$segment[0]] = ($decode_url == TRUE) ? urldecode($segment[1]) : $segment[1];
            }
        }
    }
        
    return $arr;
}

I hope someone may find this useful.

#2
[eluser]m4rw3r[/eluser]
Seems to be very cool to be able to send parameters like that.

Also, the auto-detection of segements feature is a very good thing.

#3
[eluser]TheFuzzy0ne[/eluser]
I'm glad you like the idea. I find it very useful when used with a search controller. You can send all, some or none of the advanced parameters through the URI, and the search function won't fall on it's behind if parameters are missing, or aren't in a specific order. It also makes advanced searches bookmarkable without having to cache results.

Thanks for your comments. Smile

#4
[eluser]m4rw3r[/eluser]
btw, use preg instead of ereg, preg is faster.

#5
[eluser]TheFuzzy0ne[/eluser]
I didn't know that. I actually thought it was slower. Hehe. Thanks for the advice.

#6
[eluser]TheFuzzy0ne[/eluser]
Ah, poop. There's a problem with the code on some servers if you try to use encoded forward slashes. On my development server, encoded forward slashes break everything at the server level, and I get issued a 404 error straight from the server.

If you need to use encoded forward slashes, you need to ensure that your server supports them. Most servers seem to be ok with %2 in the URI, but mine was not. The server needs to have the AllowEncodedSlashes set to On.

I'd be interested to get some feedback on whose server chokes when a %2 is in the URI and whose doesn't.

Thanks.

#7
[eluser]TheFuzzy0ne[/eluser]
OK, my controller methods were becoming messy with having to keep testing the generated associative array from alt_uri_to_assoc() for values that may not exist, so I thought I'd try a different approach.

I've extended the URI class, and also added support for servers that don't allow encoded slashes.

Code:
<?php

class MY_URI extends CI_URI {
    
    /**
     * Stores an array of alternative segments.
     *
     * @access private
     * @var array
     */
    var $asegments = array();
    
    /**
     * Set to FALSE if you don't want the segment value to be
     * url(en/de)code()d.
     *
     * @access public
     * @var bool $encode_urls
     */
    var $encode_urls = TRUE;
    
    /**
     * This is to help on servers that don't allow encoded slashes.
     *
     * Set this to a character combination you won't use in your uri
     * such as '::'. Don't set it if your server allows %2 in the URI.
     *
     * @access public
     * @var mixed $replace_slashes_by
     */
    var $replace_slashes_by = FALSE;
    
    function MY_URI()
    {
        parent::CI_URI();
        
        /*
         * Can't call $this->_fetch_alt_segments() from here, as the
         * uri_string doesn't exist yet.
         */
    }


    /**
     * Serves as a wrapper for $this->asegments.
     *
     * @access public
     * @param string $key the key for the segment pair you wish to retrieve.
     * @param bool $default[optional] value to return if key is not set
     * @return mixed
     */
    function asegment($key, $default=FALSE)
    {
        // By the time we can set $replace_slashes_by
        // the clas has already loaded, so we must decode
        // slashes as the values are returned
        if (isset($this->asegments[$key]))
        {
            $default = $this->asegments[$key];
            if ($this->replace_slashes_by)
            {
                $replace_slashes_by = "/" . $this->replace_slashes_by . "/";
                $default = preg_replace($replace_slashes_by, '/', $default);
            }
        }
        return $default;
    }


    /**
     * Returns the URI segments generated from an associative array
     *
     * The leading forward slash will automatically be stripped unless
     * it's specificed in the prefix.
     *
     * @access public
     * @param array $input an associative array of the segments to generate
     * @param bool $prefix Prefix to be added to the output string
     * @return string
     */
    function assoc_to_alt_uri($input=array(), $prefix=FALSE)
    {
        $str = "";
        if (is_array($input) && count($input) > 0)
        {
            foreach ($input as $key => $val)
            {
                $val = $this->_replace_slashes($val);
                $str .= "/$key:" . ($this->encode_urls == TRUE ? urlencode($val) : $val);
            }
        }
        if ($prefix)
        {
            $str = $prefix . $str;
        }
        else
        {
            $str = ltrim($str, '/');
        }
        
        $str = preg_replace('/[\/]+/', '/', $str);

        return $str;
    }

    /**
     * Returns the input string with slashes put back.
     *
     * @access private
     * @return string
     * @param string $str
     */
    function _replace_slashes($str)
    {
        if ($this->replace_slashes_by)
        {
            $str = preg_replace('/\//', $this->replace_slashes_by, $str);
        }
        return $str;
    }
    /**
     * Generates the asegment associative array from $this->uri_string.
     *
     * @access private
     * @return void
     */
    function _fetch_alt_segments()
    {
        $segments = explode('/', $this->uri_string());

        if (is_array($segments) && count($segments > 0))
        {
            foreach ($segments as $segment)
            {
                $segment = explode(':', $segment, 2);
                
                if (count($segment) == 2 && preg_match('/[a-z0-9\-_]+/i', $segment[0]))
                {
                    $this->asegments[$segment[0]] = ($this->encode_urls == TRUE) ? urldecode($segment[1]) : $segment[1];

                }
            }
            
        }
    }


    /**
     * As this is the last URI function called by the Router class this
     * override function simply serves to initialize the asegment array.
     *
     * @access private
     * @return void
     */
    function _reindex_segments()
    {
        parent::_reindex_segments();
        $this->_fetch_alt_segments();
    }
}

// End of file: MY_URI.php
// Location: ./system/application/libraries/MY_URI.php

EDIT: Fixed a serious bug with the replacement of slashes.

#8
[eluser]Jürgen Mutwalek[/eluser]
Hey LabTechnician,,

first thank you for your surely nice done class :-)
But could you maybe post a example of using ?
I don't get it working ... maybe there is a mistake in loading the class, or in using the functions, i don't know

Thank you
regards Jürgen

#9
[eluser]Thorpe Obazee[/eluser]
Lab Technician?

I hope I get promoted to Lab Technician! :|

#10
[eluser]TheFuzzy0ne[/eluser]
[quote author="Jürgen Mutwalek" date="1236828858"]
But could you maybe post a example of using ?
[/quote]

Hi, Jürgen!

I'm sorry, I must have missed these last two posts. I will work on an example later tonight, and post back.


Digg   Delicious   Reddit   Facebook   Twitter   StumbleUpon  


  Theme © 2014 iAndrew  
Powered By MyBB, © 2002-2021 MyBB Group.