Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve query params manipulation #2638

Merged
Merged
94 changes: 94 additions & 0 deletions src/Modifiers/CoreModifiers.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,35 @@ public function add($value, $params, $context)
return $value + $this->getMathModifierNumber($params, $context);
}

/**
* Adds a query param matching the specified key/value pair.
*
* @param $value
* @param $params
* @return string
*/
public function addQueryParam($value, $params)
{
if (isset($params[0])) {
// Remove anchor from the URL.
$url = strtok($value, '#');

// Get the anchor value an preprend it with a "#" if a value is retrieved.
$fragment = parse_url($value, PHP_URL_FRAGMENT);
$anchor = is_null($fragment) ? '' : "#{$fragment}";

// If a "?" is present in the URL, it means we should prepend "&" to the query param. Else, prepend "?".
$character = (strpos($value, '?') !== false) ? '&' : '?';

// Build the query param. If the second param is not set, just set the value as empty.
$queryParam = "{$params[0]}=" . ($params[1] ?? '');

$value = "{$url}{$character}{$queryParam}{$anchor}";
}

return $value;
}

/**
* Creates a sentence list from the given array and the ability to set the glue.
*
Expand Down Expand Up @@ -1521,6 +1550,38 @@ public function removeLeft($value, $params)
return Stringy::removeLeft($value, Arr::get($params, 0));
}

/**
* Removes a query param matching the specified key if it exists.
*
* @param $value
* @param $params
* @return string
*/
public function removeQueryParam($value, $params)
{
if (isset($params[0])) {
// Remove query params (and any following anchor) from the URL.
$url = strtok($value, '?');
$url = strtok($url, '#');

// Parse the URL to retrieve the possible query string and anchor.
$parsedUrl = parse_url($value);

// Get the anchor value an preprend it with a "#" if a value is retrieved.
$anchor = isset($parsedUrl['fragment']) ? "#{$parsedUrl['fragment']}" : '';

// Build an associative array based on the query string.
parse_str($parsedUrl['query'] ?? '', $queryAssociativeArray);

// Remove the query param matching the specified key.
unset($queryAssociativeArray[$params[0]]);

$value = $url . (empty($queryAssociativeArray) ? '' : '?' . http_build_query($queryAssociativeArray)) . $anchor;
}

return $value;
}

/**
* Returns a new string with the suffix $params[0] removed, if present.
*
Expand Down Expand Up @@ -1694,6 +1755,39 @@ public function sentenceList($value, $params)
return Str::makeSentenceList($value, $glue, $oxford_comma);
}

/**
* Sets a query param matching the specified key/value pair.
* If the key exists, its value gets updated. Else, the key/value pair gets added.
*
* @param $value
* @param $params
* @return string
*/
public function setQueryParam($value, $params)
{
if (isset($params[0])) {
// Remove query params (and any following anchor) from the URL.
$url = strtok($value, '?');
$url = strtok($url, '#');

// Parse the URL to retrieve the possible query string and anchor.
$parsedUrl = parse_url($value);

// Get the anchor value an preprend it with a "#" if a value is retrieved.
$anchor = isset($parsedUrl['fragment']) ? "#{$parsedUrl['fragment']}" : '';

// Build an associative array based on the query string.
parse_str($parsedUrl['query'] ?? '', $queryAssociativeArray);

// Update the existing param that matches the specified key, or add it if it doesn't exist.
$queryAssociativeArray[$params[0]] = $params[1] ?? '';

$value = "{$url}?" . http_build_query($queryAssociativeArray) . $anchor;
}

return $value;
}

/**
* Because sometimes you just gotta /shrug.
*
Expand Down
1 change: 1 addition & 0 deletions src/View/Cascade.php
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ private function contextualVariables()

// Request
'current_url' => $this->request->url(),
'current_full_url' => $this->request->fullUrl(),
'current_uri' => URL::format($this->request->path()),
'get_post' => Arr::sanitize($this->request->all()),
'get' => Arr::sanitize($this->request->query->all()),
Expand Down
37 changes: 37 additions & 0 deletions tests/Modifiers/AddQueryParamTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace Tests\Modifiers;

use Statamic\Modifiers\Modify;
use Tests\TestCase;

class AddQueryParamTest extends TestCase
{
protected $baseUrl = 'https://www.google.com/search';
protected $queryParam = ['q', 'test'];

/** @test */
public function it_adds_a_new_query_param()
{
$this->assertSame("{$this->baseUrl}?q=", $this->modify($this->baseUrl, ['q']));
$this->assertSame("{$this->baseUrl}?q=test", $this->modify($this->baseUrl, $this->queryParam));
$this->assertSame("{$this->baseUrl}?sourceid=chrome&q=test", $this->modify("{$this->baseUrl}?sourceid=chrome", $this->queryParam));
$this->assertSame("{$this->baseUrl}?q=test#test", $this->modify("{$this->baseUrl}#test", $this->queryParam));
}

/** @test */
public function it_does_nothing_if_no_parameters_are_passed()
{
$this->assertSame($this->baseUrl, $this->modify($this->baseUrl));
$this->assertSame("{$this->baseUrl}#test", $this->modify("{$this->baseUrl}#test"));
}

private function modify(string $url, ?array $queryParam = null)
{
if (is_null($queryParam)) {
return Modify::value($url)->addQueryParam()->fetch();
}

return Modify::value($url)->addQueryParam($queryParam)->fetch();
}
}
38 changes: 38 additions & 0 deletions tests/Modifiers/RemoveQueryParamTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

namespace Tests\Modifiers;

use Statamic\Modifiers\Modify;
use Tests\TestCase;

class RemoveQueryParamTest extends TestCase
{
protected $baseUrl = 'https://www.google.com/search';
protected $queryParamKey = 'q';

/** @test */
public function it_removes_an_existing_query_param()
{
$this->assertSame($this->baseUrl, $this->modify("{$this->baseUrl}?q=statamic", $this->queryParamKey));
$this->assertSame("{$this->baseUrl}#test", $this->modify("{$this->baseUrl}?q=statamic#test", $this->queryParamKey));
$this->assertSame("{$this->baseUrl}?sourceid=chrome", $this->modify("{$this->baseUrl}?q=statamic&sourceid=chrome", $this->queryParamKey));
$this->assertSame("{$this->baseUrl}?sourceid=chrome", $this->modify("{$this->baseUrl}?sourceid=chrome&q=statamic", $this->queryParamKey));
}

/** @test */
public function it_does_nothing_if_the_query_param_key_does_not_exist()
{
$this->assertSame($this->baseUrl, $this->modify($this->baseUrl, $this->queryParamKey));
$this->assertSame("{$this->baseUrl}#test", $this->modify("{$this->baseUrl}#test", $this->queryParamKey));
$this->assertSame("{$this->baseUrl}?sourceid=chrome", $this->modify("{$this->baseUrl}?sourceid=chrome", $this->queryParamKey));
}

private function modify(string $url, ?string $queryParamKey = null)
{
if (is_null($queryParamKey)) {
return Modify::value($url)->removeQueryParam()->fetch();
}

return Modify::value($url)->removeQueryParam($queryParamKey)->fetch();
}
}
46 changes: 46 additions & 0 deletions tests/Modifiers/SetQueryParamTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

namespace Tests\Modifiers;

use Statamic\Modifiers\Modify;
use Tests\TestCase;

class SetQueryParamTest extends TestCase
{
protected $baseUrl = 'https://www.google.com/search';
protected $queryParam = ['q', 'test'];

/** @test */
public function it_updates_an_existing_query_param()
{
$this->assertSame("{$this->baseUrl}?q=", $this->modify("{$this->baseUrl}?q=statamic", ['q']));
$this->assertSame("{$this->baseUrl}?q=test", $this->modify("{$this->baseUrl}?q=statamic", $this->queryParam));
$this->assertSame("{$this->baseUrl}?q=test#test", $this->modify("{$this->baseUrl}?q=statamic#test", $this->queryParam));
$this->assertSame("{$this->baseUrl}?q=test&sourceid=chrome", $this->modify("{$this->baseUrl}?q=statamic&sourceid=chrome", $this->queryParam));
$this->assertSame("{$this->baseUrl}?sourceid=chrome&q=test", $this->modify("{$this->baseUrl}?sourceid=chrome&q=statamic", $this->queryParam));
}

/** @test */
public function it_adds_a_non_existant_query_param()
{
$this->assertSame("{$this->baseUrl}?q=", $this->modify($this->baseUrl, ['q']));
$this->assertSame("{$this->baseUrl}?q=test", $this->modify($this->baseUrl, $this->queryParam));
$this->assertSame("{$this->baseUrl}?q=test#test", $this->modify("{$this->baseUrl}#test", $this->queryParam));
$this->assertSame("{$this->baseUrl}?sourceid=chrome&q=test", $this->modify("{$this->baseUrl}?sourceid=chrome", $this->queryParam));
}

/** @test */
public function it_does_nothing_if_no_parameters_are_passed()
{
$this->assertSame($this->baseUrl, $this->modify($this->baseUrl));
}

private function modify(string $url, ?array $queryParam = null)
{
if (is_null($queryParam)) {
return Modify::value($url)->setQueryParam()->fetch();
}

return Modify::value($url)->setQueryParam($queryParam)->fetch();
}
}
3 changes: 2 additions & 1 deletion tests/View/CascadeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,11 @@ public function it_hydrates_dates()
/** @test */
public function it_hydrates_request_variables()
{
$this->get('/test');
$this->get('/test?test=test');

tap($this->cascade()->hydrate()->toArray(), function ($cascade) {
$this->assertEquals('http://test.com/test', $cascade['current_url']);
$this->assertEquals('http://test.com/test?test=test', $cascade['current_full_url']);
$this->assertEquals('/test', $cascade['current_uri']);
});
}
Expand Down