Keeping querystrings clean with Zend Framework

I'm something of a zealot about short, readable URLs. Most people by now are using server rewrite rules and Front Controllers of some sort to keep the paths in their application sane and legible, but an area that's often overlooked is the querystring, especially after a form submission.

A typical example of a situation with a 'messy' querystring might be a search form on site. When a form is submitted, all the elements in the form are entered into the querystring whether they're relevant or not.

That leads to querystrings like /search?size=&shape=&colour=red&age=, which would be a lot more legible as /search?colour=red

On some sites I work with, an 'advanced search form' can have up to 20 elements, all of which might get submitted in one go leaving the search results page with a huge URL.

Aside from an anally retentive need for neatness, the shorter querystrings provide the following benefits:

  1. They are easier to remember.
  2. They are less likely to wrap when sent in emails.
  3. The URL is populated with relevant keywords and not the others, so is better for SEO.
  4. By shortening the URL in this way we ensure there's one canonical URL for every search, which has SEO benefits and can help with aggregating search statistics.

So, in the interest of all of the above I've written a Zend Framework Controller Plugin to shorten any GET request that has a querystring.

What is important to note about the plugin:

  • It takes a flag in its constructor to say whether the redirects used should be permanent or temporary. This is mainly used for testing.
  • The plugin takes effect before any routing is applied, so is about as efficient as a Zend Framework application can be.
  • The plugin would be initialised in your bootstrap, by doing $front->registerPlugin(new Plugin_CleanQuery());.

Here's the source of the plugin, I'm sure the array stuff could be done a bit more efficiently with some combination of array_filter() and array_walk_recursive() but I'm not convinced it'll be a bottleneck.

<?php

/**
 * Plugin that cleans up querystrings in GET submissions
 */
class Plugin_CleanQuery extends Zend_Controller_Plugin_Abstract 
{

    
/**
     * @var boolean
     */
    
protected $permanent;
    
    
/**
     * Takes a flag in the constructor to determine whether the redirects 
     * are permanent or just temporary (default);
     *
     * @param boolean $permanent
     */
    
public function __construct($permanent=false) {
        
$this->permanent=$permanent;
    }
    
    
    
/**
     * Cleans the GET and if it's changed does a redirect
     */
    
public function routeStartup()
    {
        
        if(
$this->getRequest()->isGet()) {
            if(
$params $this->getRequest()->getParams()) {
                
$new_params $this->_filterArray($params);
                if(
count($paramsCOUNT_RECURSIVE)
                   >
count($new_paramsCOUNT_RECURSIVE)){
                    
$uri $this->getRequest()->getRequestUri();
                    
$qs substr($uri0strpos($uri'?')+1
                        . 
http_build_query($new_params);
                    
$this->getResponse()->setRedirect($qs
                        
$this->permanent?301:302);
                    
$this->getResponse()->sendResponse();
                }
            }
        }
        
    }

    
/**
     * Cleans out false values from an array
     *
     * @param array $array
     */
    
private function _filterArray($array)
    {
        foreach(
$array as $key=>$value) {
            if(
is_array($value)) {
                
$value $this->_filterArray($value);
                
$array[$key]=$value;
            }
            if(
$value==='') {
                unset(
$array[$key]);
            }
        }
        return 
$array;
    }
    
    
}
Bookmark and Share

Comments

1.

I forgot to add, I'd consider this code public domain so anyone can use/modify/redistribute it.

Ciaran McNulty
8th December 2008, 12:26

2.

I would suggest replacing your code with what i wrote below. It's much more clean and less prone to errors. One could argue that using the routeShutdown instead of the Startup could lead to some overhead (because the route is precessed), but i guess ultimately it depends on the volume of traffic that goes through this, and that may be site dependent. For grid filters in an admin for example, this would be the better solution.

public function routeShutdown( Zend_Controller_Request_Abstract $request )
{
if( count( $request->getQuery() ) )
{
$router = Zend_Controller_Front::getInstance()->getRouter();
$url = $router->assemble( array_reverse( $request->getQuery(), true ), null, false, true );

$this->getResponse()->setRedirect( $url )->sendResponse();
exit;
}
}

Alex
26th January 2009, 22:38

3.

Interesting stuff, Alex! I wasn't aware you could play with the Router like that, it would be interesting to compare the overheads of doing this pre/post routing.

Ciaran McNulty
29th January 2009, 09:20

4.

this article and your comments are very helpful to me. thank you!

Marc
14th January 2010, 22:12

Add a comment