Welcome Guest, Not a member yet? Register   Sign In
Query String URL Enhancement
#1

[eluser]freshmug[/eluser]
Hi everybody,

I had a problem described in my last post:
http://ellislab.com/forums/viewthread/68197/

This problem forced me to use query strings and the url I had to parse was as follows:
http://www.mysite.com/index.php?c=mycont...6bab9832c4

I enabled query strings in my config file:
$config['enable_query_strings'] = TRUE;
$config['controller_trigger'] = 'c';
$config['function_trigger'] = 'm';

I still had issues getting to the auth_token value.

In the end I changed some code in the Router.php file and wanted to submit the code here in case anyone else finds it useful. It makes query strings act pretty much the same as the regular CodeIgniter URLs. For example, if you had a controller like:

Code:
class myClass extends Controller {
   function myFunction ($myValue) {
   }
}

Normally, you could call myFunction with a value using a CodeIgniter URL:

Code:
www.mySite.com/index.php/myClass/myFunction/123456

With the changes I made to router.php, you can now call it with:

Code:
www.mySite.com/index.php?c=myClass&m=myFunction&value=123456

The way the changed code is written now, you only need the "controller_trigger" key in the query string. The other keys do not matter. The values will go into the segment array in the order they show up in the query string.

There are a few improvements that could be made but what's there now is a good start.

I can't seem to attach the changed Router.php file (renaming or zipping didn't seem to matter). But I only changed the _set_route_mapping() function commenting out the following code at the top:

Code:
/*
        // Are query strings enabled in the config file?
        // If so, we're done since segment based URIs are not used with query strings.
        if ($this->config->item('enable_query_strings') === TRUE AND isset($_GET[$this->config->item('controller_trigger')]))
        {
            $this->set_class(trim($this->_filter_uri($_GET[$this->config->item('controller_trigger')])));

            if (isset($_GET[$this->config->item('function_trigger')]))
            {
                $this->set_method(trim($this->_filter_uri($_GET[$this->config->item('function_trigger')])));
            }

            return;
        }
*/

and adding the following code near the end of the function:

Code:
// check if query strings enabled
        if ($this->config->item('enable_query_strings') === TRUE AND isset($_GET[$this->config->item('controller_trigger')]))
        {
            // at this point the uri_string is the query string
            $keyValues = split('&', $this->uri_string);
            foreach($keyValues as $keyValue) {
                list($key, $value) = split('=', $keyValue, 2);

                // just use the value and add in the order they come in
                // at some point may want to utilize keys and ignore order

                $value = trim($this->_filter_uri($value));

                if ($value != '')
                    $this->segments[] = $value;
            }

        } else {

the code above goes just above:

Code:
// Explode the URI Segments. The individual segments will
            // be stored in the $this->segments array.  
            foreach(explode("/", preg_replace("|/*(.+?)/*$|", "\\1", $this->uri_string)) as $val)
            {
                // Filter segments for security
                $val = trim($this->_filter_uri($val));

                if ($val != '')
                    $this->segments[] = $val;
            }

        } // do not forget this bracket

make sure to add the closing bracket


Hope someone finds it useful. Smile
#2

[eluser]freshmug[/eluser]
Hi everyone,

Just thought I'd share the following code. It works with the 1.6.1 version of CI and is a cleaner way to achieve full query string support as described in my first post above.

Put the following in your system/application/config/config.php file:
Code:
/*
|--------------------------------------------------------------------------
| Enable Full Query Strings
|--------------------------------------------------------------------------
|
| Full query string support similar to segment based URLs.
|
| **** IMPORTANT **** enable_query_strings above must be set to FALSE
|
| Still uses the trigger configruations above.
*/
$config['enable_full_query_strings'] = TRUE; // enable_query_strings must be false

'enable_query_strings' must be FALSE and 'enable_full_query_strings' must be TRUE for things to work.

Next we'll extend the CI_URI class by adding a file called MY_URI.php to system/application/libraries/ with the following code in it:
Code:
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');

class MY_URI extends CI_URI {

    function MY_URI() {
        parent::CI_URI();
    }

    /**    
     * Override for better query string support in CI.
     *
     * Explode the URI Segments. The individual segments will
     * be stored in the $this->segments array.  
     *  
     * @access  private
     * @return  void
     */
    function _explode_segments()
    {
        // check if query strings enabled
        if ($this->config->item('enable_query_strings') === FALSE AND $this->config->item('enable_full_query_strings') === TRUE AND isset($_GET[$this->config->item('controller_trigger')]))
        {
            // at this point the uri_string is the query string
            $keyValues = split('&', $this->uri_string);
            foreach($keyValues as $keyValue) {
                list($key, $value) = split('=', $keyValue, 2);

                // just use the value and add in the order they come in
                // at some point may want to utilize keys and ignore order

                $value = trim($this->_filter_uri($value));

                if ($value != '')
                    $this->segments[] = $value;
            }

        } else {

            // Explode the URI Segments. The individual segments will
            // be stored in the $this->segments array.  
            foreach(explode("/", preg_replace("|/*(.+?)/*$|", "\\1", $this->uri_string)) as $val)
            {
                // Filter segments for security
                $val = trim($this->_filter_uri($val));

                if ($val != '')
                    $this->segments[] = $val;
            }

        }
    }

}

?>

That's it. Now you can make requests like:
http://localhost/index.php?c=tester&m=qu...10&arg2=20

Note this would be the same as a CI segement request like:
http://localhost/tester/queryStringArgTest/10/20

Also note the current implementation only cares about the order of the query string values ... it doesn't care what the key names are. But the implementation does check to see if the "controller_trigger" exists, so only that key must be present. Something like the following would work the same as the example above:
http://localhost/index.php?c=tester&foo=...=10&foo=20

Eventually I may add some more capability such as mapping the querystring keys to arguments so they could be out of order. But what I've got now works for me.

I haven't fully tested the code but I'll let you know if I run into any issues.

I'm learning more and more about the CI framework as I use it. I'm loving it. Thanks for putting it out there for people like me to utilize.
#3

[eluser]webmogul[/eluser]
I kind of glanced over the code portion, but if I follow you correctly, using your extension here I could have a URL that supports regular query strings and the rest of my urls be friendly correct?

I <b>really</b> need something like this for a search function. The problem is that not every option in a search box is enabled and it makes it really error prone to figure which options were chosen with the segment only URLs. Think of an advanced search capability on a site with different options.

Something even better would be mixed mode. Where I could have host.com/view_items/productName/search?minPrice=1&maxPrice=100&blah;=&blah;=. Grails allows this and it is quite nice, but I can't use grails Smile

regardless, having an either/or solution would be wonderful.
#4

[eluser]kaosweaver[/eluser]
THANK YOU, this is great! it worked perfectly and got my project to the next step. Saved me tons of time (and impressed my boss).

I hope they incorporate this in to the CI core.
#5

[eluser]domonline[/eluser]
Thought I share this as I found it incredibly frustrating that I couldn't mix using paths and query strings and a situation arose that demanded this with very short time frame to sort it out.

If you want urls like:

Code:
http://www.yoursite.com/segment/and_so_on/?query=1&query1=2

This'll work like you'd expect it.

e.g. you can still use :

Code:
$this->uri->segment(2)

// but you can also use

$_GET['query']

and

Code:
http://www.yoursite.com/segment/and_so_on // Works
http://www.yoursite.com/index.php?c=food&m=menu // Works and pulls up crontroller/method
http://www.yoursite.com/segment/and_so_on/?query=working // Also works

You need to create a file called MY_URI.php in your system/application/libraries/ folder

Put the following in it, it's really simple just overrides one method.

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

class MY_URI extends CI_URI {

    function MY_URI() {
        parent::CI_URI();
    }

    // --------------------------------------------------------------------
    
    /**
     * Override the _parse_request_uri method so it allows query strings through
     *
     * @access    private
     * @return    string
     */    
    function _parse_request_uri()
    {
        if ( ! isset($_SERVER['REQUEST_URI']) OR $_SERVER['REQUEST_URI'] == '')
        {
            return '';
        }
        
        $uri = explode("?",$_SERVER['REQUEST_URI']); // This line is added to the original
        $request_uri = preg_replace("|/(.*)|", "\\1", str_replace("\\", "/", $uri[0])); // This line changed
        // Everything else is just the same

        if ($request_uri == '' OR $request_uri == SELF)
        {
            return '';
        }
        
        $fc_path = FCPATH;        
        if (strpos($request_uri, '?') !== FALSE)
        {
            $fc_path .= '?';
        }
        
        $parsed_uri = explode("/", $request_uri);
                
        $i = 0;
        foreach(explode("/", $fc_path) as $segment)
        {
            if (isset($parsed_uri[$i]) AND $segment == $parsed_uri[$i])
            {
                $i++;
            }
        }
        
        $parsed_uri = implode("/", array_slice($parsed_uri, $i));
        
        if ($parsed_uri != '')
        {
            $parsed_uri = '/'.$parsed_uri;
        }

        return $parsed_uri;
    }

}

?&gt;

In your system/application/config.php file you need to enable query strings so $_GET isn't unset and set the uri protocol to REQUEST_URI. With some digging around I'm sure you could get AUTO and the other methods to work as well but just didn't have the time or the need.

Code:
$config['uri_protocol']    = "REQUEST_URI";
$config['enable_query_strings'] = TRUE;

I hope this is of help to someone
#6

[eluser]wiredesignz[/eluser]
Why not simply enable_query_strings, use PATH_INFO, and use $this->input->get('variable'); in the Controller
Code:
http://domain.tld/index.php/controller_name/method_name?variable=stop-messing-around
#7

[eluser]domonline[/eluser]
Wow that works on my live machine perfectly thanks for that. It doesn't work on my local one, god knows why, probably something I've done no doubt.

I wish it was easier to find your answer though. When you google about query strings in codeigniter you just get posts about having to hack away to be able to get them to work which I now see is actually misleading.

Thanks wiredesignz
#8

[eluser]mattalexx[/eluser]
[quote author="wiredesignz" date="1209019272"]Why not simply enable_query_strings, use PATH_INFO, and use $this->input->get('variable'); in the Controller
Code:
http://domain.tld/index.php/controller_name/method_name?variable=stop-messing-around
[/quote]

You know that feeling you get when you delete a bunch of code you don't need anymore? Well it's one of my favorite things. Thanks, wiredesignz.

I want to clarify what wiredesignz means by "use PATH_INFO". I am not fluent in CI config and I had to research what he meant. He meant to change this:
Code:
$config['uri_protocol']    = "AUTO";
To this:
Code:
$config['uri_protocol']    = "PATH_INFO";
... in your APP/config/config.php

Another thing: I found that PATH_INFO was not reliably present on my server, so I used REQUEST_URI instead. It's been working fine for the five minutes since I made the change. We'll see.




Theme © iAndrew 2016 - Forum software by © MyBB