diff --git a/src/Dto/SearchDto.php b/src/Dto/SearchDto.php index 17ab06d38d..d0c6f35a2a 100644 --- a/src/Dto/SearchDto.php +++ b/src/Dto/SearchDto.php @@ -2,6 +2,7 @@ namespace EasyCorp\Bundle\EasyAdminBundle\Dto; +use EasyCorp\Bundle\EasyAdminBundle\Config\Option\EA; use EasyCorp\Bundle\EasyAdminBundle\Config\Option\SearchMode; use Symfony\Component\HttpFoundation\Request; @@ -137,4 +138,37 @@ public function getSearchMode(): string { return $this->searchMode; } + + /** + * Returns the query string parameters that must be preserved when submitting + * the index search form. Browsers discard the query string of GET forms, so + * these parameters are rendered as hidden fields in the search form template. + * + * @return array + */ + public function getPreservedQueryParameters(): array + { + $parameters = $this->request->query->all(); + + foreach ([ + EA::QUERY, + EA::PAGE, + EA::FILTERS, + EA::CRUD_ACTION, + EA::CRUD_CONTROLLER_FQCN, + EA::DASHBOARD_CONTROLLER_FQCN, + EA::ENTITY_FQCN, + EA::ENTITY_ID, + EA::ROUTE_NAME, + EA::ROUTE_PARAMS, + EA::BATCH_ACTION_NAME, + EA::BATCH_ACTION_URL, + EA::BATCH_ACTION_CSRF_TOKEN, + EA::BATCH_ACTION_ENTITY_IDS, + ] as $reservedParameter) { + unset($parameters[$reservedParameter]); + } + + return $parameters; + } } diff --git a/templates/layout.html.twig b/templates/layout.html.twig index 3b055c009e..c7461e5ff4 100644 --- a/templates/layout.html.twig +++ b/templates/layout.html.twig @@ -300,6 +300,12 @@ {% endif %} {% endfor %} + + {# Browsers remove the query string when submitting forms using GET; + that's why the remaining query string parameters are added as hidden form fields #} + {% for paramName, paramValue in ea.search.preservedQueryParameters|ea_flatten_array %} + + {% endfor %} {% endblock %}
diff --git a/tests/Functional/Default/Search/SearchAllTermsTest.php b/tests/Functional/Default/Search/SearchAllTermsTest.php index bf102b38a6..0d3dd3f406 100644 --- a/tests/Functional/Default/Search/SearchAllTermsTest.php +++ b/tests/Functional/Default/Search/SearchAllTermsTest.php @@ -69,6 +69,21 @@ public function testSearchFormAfterMakingAQuery(): void $this->assertStringStartsWith($this->generateIndexUrl(), $resetUrl); } + public function testSearchPreservesCustomQueryParameters(): void + { + $crawler = $this->client->request('GET', $this->getCrudUrl('index', null, ['customContext' => 'test'])); + + $form = $crawler->filter('form.form-action-search'); + $hiddenInput = $form->filter('input[type="hidden"][name="customContext"]'); + $this->assertCount(1, $hiddenInput); + $this->assertSame('test', $hiddenInput->attr('value')); + + $this->client->submit($form->form(['query' => 'PHP'])); + + $this->assertStringContainsString('customContext=test', $this->client->getRequest()->getUri()); + $this->assertStringContainsString('query=PHP', $this->client->getRequest()->getUri()); + } + public function testSearchIsPersistedAfterPaginationAndSorting(): void { // make some query diff --git a/tests/Unit/Dto/SearchDtoTest.php b/tests/Unit/Dto/SearchDtoTest.php index a1d73725e8..dd53e5e04f 100644 --- a/tests/Unit/Dto/SearchDtoTest.php +++ b/tests/Unit/Dto/SearchDtoTest.php @@ -2,6 +2,7 @@ namespace EasyCorp\Bundle\EasyAdminBundle\Tests\Unit\Dto; +use EasyCorp\Bundle\EasyAdminBundle\Config\Option\EA; use EasyCorp\Bundle\EasyAdminBundle\Config\Option\SearchMode; use EasyCorp\Bundle\EasyAdminBundle\Dto\SearchDto; use PHPUnit\Framework\TestCase; @@ -78,6 +79,41 @@ public static function provideSearchModeTests(): iterable yield 'all terms search mode' => [SearchMode::ALL_TERMS]; } + /** + * @dataProvider providePreservedQueryParametersTests + */ + public function testGetPreservedQueryParameters(array $queryParameters, array $expectedPreservedParameters): void + { + $request = new Request($queryParameters); + $dto = new SearchDto($request, null, $queryParameters[EA::QUERY] ?? null, [], [], null); + + $this->assertSame($expectedPreservedParameters, $dto->getPreservedQueryParameters()); + } + + public static function providePreservedQueryParametersTests(): iterable + { + yield 'custom query parameters are preserved' => [ + ['query' => 'foo', 'page' => '2', 'customContext' => 'test'], + ['customContext' => 'test'], + ]; + + yield 'sort parameters are preserved' => [ + ['query' => 'foo', 'sort' => ['title' => 'ASC']], + ['sort' => ['title' => 'ASC']], + ]; + + yield 'filters and reserved parameters are excluded' => [ + [ + 'query' => 'foo', + 'page' => '2', + 'filters' => ['title' => ['comparison' => '=', 'value' => 'bar']], + EA::CRUD_ACTION => 'index', + 'customContext' => 'test', + ], + ['customContext' => 'test'], + ]; + } + public static function provideSortDirectionTests(): iterable { yield 'no default sort, no custom sort' => [