diff --git a/src/Modifiers/CoreModifiers.php b/src/Modifiers/CoreModifiers.php index e68447ebb8..d587dd6aac 100644 --- a/src/Modifiers/CoreModifiers.php +++ b/src/Modifiers/CoreModifiers.php @@ -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. * @@ -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. * @@ -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. * diff --git a/src/View/Cascade.php b/src/View/Cascade.php index cb39126c3a..199eac87e6 100644 --- a/src/View/Cascade.php +++ b/src/View/Cascade.php @@ -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()), diff --git a/tests/Modifiers/AddQueryParamTest.php b/tests/Modifiers/AddQueryParamTest.php new file mode 100644 index 0000000000..1f95968224 --- /dev/null +++ b/tests/Modifiers/AddQueryParamTest.php @@ -0,0 +1,37 @@ +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(); + } +} diff --git a/tests/Modifiers/RemoveQueryParamTest.php b/tests/Modifiers/RemoveQueryParamTest.php new file mode 100644 index 0000000000..83174366ab --- /dev/null +++ b/tests/Modifiers/RemoveQueryParamTest.php @@ -0,0 +1,44 @@ +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)); + } + + /** @test */ + public function it_does_nothing_if_no_parameters_are_passed() + { + $this->assertSame($this->baseUrl, $this->modify($this->baseUrl)); + } + + 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(); + } +} diff --git a/tests/Modifiers/SetQueryParamTest.php b/tests/Modifiers/SetQueryParamTest.php new file mode 100644 index 0000000000..9f572a3eb7 --- /dev/null +++ b/tests/Modifiers/SetQueryParamTest.php @@ -0,0 +1,46 @@ +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(); + } +} diff --git a/tests/View/CascadeTest.php b/tests/View/CascadeTest.php index 8616cd0a82..75c9aaea9b 100644 --- a/tests/View/CascadeTest.php +++ b/tests/View/CascadeTest.php @@ -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']); }); }