diff --git a/src/Metadata/Tests/Util/IriHelperTest.php b/src/Metadata/Tests/Util/IriHelperTest.php index 5fcc2ac98d..ead4540ed2 100644 --- a/src/Metadata/Tests/Util/IriHelperTest.php +++ b/src/Metadata/Tests/Util/IriHelperTest.php @@ -70,6 +70,41 @@ public function testHelpersWithNetworkPath(): void $this->assertSame('//foo:bar@localhost:443/hello.json?foo=bar&bar=3&page=2#foo', IriHelper::createIri($parsed['parts'], $parsed['parameters'], 'page', 2., UrlGeneratorInterface::NET_PATH)); } + public function testHelpersPreserveNestedArrayQueryParameters(): void + { + $parts = [ + 'path' => '/objects', + 'query' => '', + ]; + $parameters = [ + 'filters' => [ + 0 => ['a' => 'foo', 'b' => 'bar'], + ], + ]; + + $iri = IriHelper::createIri($parts, $parameters, 'page', 2.); + + $this->assertSame('/objects?filters%5B0%5D%5Ba%5D=foo&filters%5B0%5D%5Bb%5D=bar&page=2', $iri); + + $reparsed = IriHelper::parseIri($iri, 'page'); + $this->assertSame($parameters, $reparsed['parameters']); + } + + public function testHelpersCollapseSimpleListQueryParameters(): void + { + $parts = [ + 'path' => '/objects', + 'query' => '', + ]; + $parameters = [ + 'foo' => ['a', 'b'], + ]; + + $iri = IriHelper::createIri($parts, $parameters, 'page', 2.); + + $this->assertSame('/objects?foo%5B%5D=a&foo%5B%5D=b&page=2', $iri); + } + public function testParseIriWithInvalidUrl(): void { $this->expectException(InvalidArgumentException::class); diff --git a/src/Metadata/Util/IriHelper.php b/src/Metadata/Util/IriHelper.php index 7803562cf9..61c2d61f88 100644 --- a/src/Metadata/Util/IriHelper.php +++ b/src/Metadata/Util/IriHelper.php @@ -65,7 +65,10 @@ public static function createIri(array $parts, array $parameters, ?string $pageP } $query = http_build_query($parameters, '', '&', \PHP_QUERY_RFC3986); - $parts['query'] = preg_replace('/%5B\d+%5D/', '%5B%5D', $query); + // Only collapse a numeric index when it is the leaf segment of a bracket chain + // (a simple list element). Collapsing a non-leaf index would merge distinct keys of a + // nested array into separate elements (e.g. filters[0][a] must stay filters[0][a]). + $parts['query'] = preg_replace('/%5B\d+%5D(?!%5B)/', '%5B%5D', $query); $url = ''; if ((UrlGeneratorInterface::ABS_URL === $urlGenerationStrategy || UrlGeneratorInterface::NET_PATH === $urlGenerationStrategy) && isset($parts['host'])) {