61

On laravel 4 I could generate a url with query strings using the route() helper. But on 4.1 instead of:

$url = url('admin.events', array('lang' => 'en'));
// admineventsurl/?lang=en

I get:

$url = url('admin.events', array('lang' => 'en'));
// admineventsurl/en

I did some research and all laravel methods to generate url are using the parameters like that. How can I generate the url with query strings?

5 Answers 5

120

Laravel's route() and action() helper methods support URL query params. The url() helper method, unfortunately does not.

Simply provide an array with key => value pairs to the route parameters. For example:

route('products.index', ['manufacturer' => 'Samsung']);

// Returns 'http://localhost/products?manufacturer=Samsung'

You can also still include your route parameters (such as ID's and models) to accompany these parameters:

route('products.show', [$product->id, 'model' => 'T9X']);

// Returns 'http://localhost/products/1?model=T9X'

Basically, any elements in the array that contain string keys will be treated as query parameter (/products?param=value). Anything with an integer array key will be treated as a URL argument (/products/{arg}).

This is also supported in action methods:

action('ProductController@index', ['manufacturer' => 'Samsung']);

You can also supply query parameters inside the link_to_route() and link_to_action() methods:

link_to_route('products.index', 'Products by Samsung', ['model' => 'Samsung');

link_to_action('ProductController@index', 'Products by Samsung', ['model' => 'Samsung']);

2019 - EDIT:

If you can't use route() or action(), you can generate a URL with query params using the Arr::query() helper:

url('/products?').\Illuminate\Support\Arr::query(['manufacturer' => 'Samsung']);

// Returns 'http://localhost/products?manufacturer=Samsung'

Or:

url('/products?').http_build_query(['manufacturer' => 'Samsung'], null, '&', PHP_QUERY_RFC3986);

// Returns 'http://localhost/products?manufacturer=Samsung'

Or create a simple helper function:

use Illuminate\Support\Arr;
use Illuminate\Support\Str;

function url_query($to, array $params = [], array $additional = []) {
    return Str::finish(url($to, $additional), '?') . Arr::query($params);
}

Then call it:

url_query('products', ['manufacturer' => 'Samsung']);

// Returns 'http://localhost/products?manufacturer=Samsung'

url_query('products', ['manufacturer' => 'Samsung'], [$product->id]);

// Returns 'http://localhost/products/1?manufacturer=Samsung'
Sign up to request clarification or add additional context in comments.

1 Comment

I disagree - url paths should contain only parameters to a specific resource, and query string parameters are available to modify that request in some non-essential way. It could be argued that a page rendered in a different language is an entirely different resource, rather than a modified version, but I wouldn't accept that. Regardless of SEO tricks, semantics are the most important part of structuring an application, and this strikes me as a non-semantic approach.
44

Side note.

I disagree with @Steve Bauman's idea (in his answer) that one rarely needs querystring urls, and think that Laravel should at least consider adding querystring functionality (back) in. There are plenty of cases when you want a querystring url rather than a param based "pretty url". For example, a complex search filter...

example.com/search/red/large/rabid/female/bunny

...may potentially refer to the same exact set of rodents as...

example.com/search/bunny/rabid/large/female/red

...but any way you look at it (programming, marketing analytics, SEO, user-friendliness), it's kinda terrible. Even though...

example.com/search?critter=bunny&gender=female&temperament=rabid&size=large&color=red

...is longer and "uglier", it actually is better in this not-so-rare case. Net: Friendly URLs are great for some things, querystrings are great for others.

Answer to the original question...

I needed a "querystring" version of url() -- so I copied the function, modified it, and stuck it in /app/start/global.php:

/**
 * Generate a querystring url for the application.
 *
 * Assumes that you want a URL with a querystring rather than route params
 * (which is what the default url() helper does)
 *
 * @param  string  $path
 * @param  mixed   $qs
 * @param  bool    $secure
 * @return string
 */
function qs_url($path = null, $qs = array(), $secure = null)
{
    $url = app('url')->to($path, $secure);
    if (count($qs)){

        foreach($qs as $key => $value){
            $qs[$key] = sprintf('%s=%s',$key, urlencode($value));
        }
        $url = sprintf('%s?%s', $url, implode('&', $qs));
    }
    return $url;
}

Example:

$url = qs_url('sign-in', array('email'=>$user->email));
//http://example.loc/sign-in?email=chris%40foobar.com

Note: It appears that the url() function is pluggable, that is, you can replace it. Look in vendor/laravel/framework/src/Illuminate/Support/helpers.php: the url function is wrapped in a if ( ! function_exists('url')) conditional. But you would probably have to jump through hoops to do it (i.e. have laravel load it before its version.)

Cheers,

Chris

2 Comments

@DiegoCastro Have a look at my answer below - it helps to set up routes in the standard Laravel way, but without creating a global function or avoiding the framework. Would love to hear what you think.
I can't get "red large rabid female bunny" out of my head now. It's a memetic hazard.
1

The following was what I needed to do:

I handle all of my routing in a service provider, where I had defined the following function:

private function registerRestfulController($prefix, $controllerClass)
{
    Route::controller($prefix, $controllerClass, $controllerClass::getRouteNames());
}

getRouteNames is a static method on my BaseController that conventionally returns routes so that RESTful controllers can have automatic named routes.

The problem I was running into was that this defined the set of wildcard matchers on the route itself - in order to avoid that, I add the following to the private function above:

foreach ($controllerClass::getRoutesNames() as $name) { 
    $route = Route::getRoutes()->getByName($name);
    $cleanUri = preg_replace('/\/\{\w*\?\}/', '', $route->getUri());
    $route->setUri($cleanUri);
}

This loads all the routes you are registering at the time and immediately removes wildcards from the URI. You could easily pass a boolean or "white-list" of route names that you want to preserve wildcards for, so that it doesn't stomp all over the Laravel default without the intention. Once you run this, it automatically starts working with query string variables, which I find far preferable to path variables in this instance.

Comments

-1

A simple way to do this, specially to use with jQuery Autocomplete, it's modify the Controller with a condition to check if has 'term' in the $request:

(Controller file)

public function list_for_autocomplete(Request $request)
{
    if ($request->has('term')) {
        return YourModel::select('column_name as value')
            ->where('column_name', 'like', '%' . $request->input('term') . '%')
            ->get()
    }
}

Comments

-1
$query = implode([
    route('your_route_name'),
    '?',
    http_build_query(['param1'=>'def','param2'=>'def'])
]);

Vanilla php resolve that without worries

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.