spatie/query-string
Manipulate URLs by building and updating query strings in PHP. Toggle parameters or values, add JSON:API-style filters and sorts, and manage pagination. Useful for generating links and keeping state in apps without manual query parsing.
composer require spatie/query-string
QueryString class and instantiate it with a URI:
use Spatie\QueryString\QueryString;
$queryString = new QueryString('https://example.com/path?existing=param');
$uri = $queryString->toggle('filter'); // Adds `?filter` if missing, removes it if present
toggle(), add(), remove(), and get().src/QueryString.php for method signatures and edge-case handling (e.g., malformed URIs).tests/ for real-world examples (e.g., handling nested routes, special characters).Use QueryString to dynamically adjust query parameters based on user actions (e.g., toggling filters):
public function toggleFilter(Request $request, string $filter)
{
$uri = new QueryString($request->fullUrl());
$uri->toggle($filter);
return redirect($uri->get());
}
Build URLs with optional query strings (e.g., for pagination or sorting):
$baseUrl = route('products.index');
$query = new QueryString($baseUrl);
$query->add('sort', 'price')->add('page', 2);
$url = $query->get(); // "/products?sort=price&page=2"
Url FacadeCombine with Laravel’s Url helper for seamless URL generation:
$url = Url::current();
$query = new QueryString($url);
$query->remove('utm_source'); // Clean up tracking params
return redirect($query->get());
Preserve existing query parameters when submitting forms:
// In a Blade template
<form action="{{ $queryString->add('action', 'search')->get() }}">
Modify Location headers in API responses to include/exclude query params:
return response()->redirectTo($queryString->toggle('debug')->get());
Use the package to validate incoming query strings (e.g., whitelist allowed params):
$allowedParams = ['page', 'per_page', 'sort'];
$query = new QueryString($request->fullUrl());
$cleanUrl = $query->filter(fn($key) => in_array($key, $allowedParams))->get();
Handle nested or complex query structures (e.g., arrays):
$query->set('filters', ['category' => 'books', 'price' => ['min' => 10]]);
// Result: "?filters[category]=books&filters[price][min]=10"
Mock query strings in tests to simulate user interactions:
$query = new QueryString('https://example.com?old=param');
$this->assertEquals('https://example.com?new=param', $query->remove('old')->add('new', 'param')->get());
URI Parsing Quirks
? before params) may break parsing.
Fix: Use QueryString::createFromUriString() or validate input with parse_url().$query = new QueryString('https://example.compath?param=1'); // Fails silently
// Solution: Sanitize input first.
Parameter Overwriting
add() or set() overwrites existing values without warning.
Fix: Use toggle() for boolean flags or check with has() first:
if (!$query->has('debug')) {
$query->add('debug', 'true');
}
Encoding/Decoding Mismatches
&, =) in values may cause parsing errors.
Fix: Manually encode values before passing to add()/set():
$query->add('search', urlencode('foo & bar'));
Fragment Handling
# (fragments) are ignored.
Fix: Strip fragments before processing:
$uriWithoutFragment = strtok($uri, '#');
$query = new QueryString($uriWithoutFragment);
Laravel-Specific Conflicts
Illuminate\Support\Str or Url facade methods.
Fix: Prefix methods or use fully qualified names:
\Spatie\QueryString\QueryString::createFromUriString($uri);
Inspect Raw Input Log the input URI to debug parsing issues:
\Log::debug('Raw URI:', ['uri' => $uri]);
Check for Silent Failures
Always validate the output with get() after modifications:
$uri = $query->toggle('param')->get();
$this->assertStringContainsString('param=', $uri);
Test Edge Cases Test with:
/).? characters (http://example.com??param=1).?search=café).Custom Parameter Handlers Extend the class to add domain-specific logic (e.g., auto-encoding for API params):
class ApiQueryString extends QueryString
{
public function addApiParam(string $key, $value): self
{
return $this->add($key, json_encode($value));
}
}
Integration with Laravel Services Bind the package to Laravel’s service container for dependency injection:
// In a service provider
$this->app->bind(QueryString::class, function () {
return new QueryString(request()->fullUrl());
});
Middleware for Query String Sanitization Create middleware to clean query strings globally:
public function handle($request, Closure $next)
{
$uri = new QueryString($request->fullUrl());
$cleanUri = $uri->filter(fn($key) => !str_starts_with($key, 'utm_'))->get();
$request->merge(pathinfo($cleanUri, PATH_QUERY));
return $next($request);
}
Event-Based Query String Changes Trigger events when query strings are modified (e.g., for analytics):
$query->onChange(function ($oldUri, $newUri) {
event(new QueryStringChanged($oldUri, $newUri));
});
// Requires extending the class to support events.
How can I help you explore Laravel packages today?