From f542df69bc98736a3183f2038378821c56809bfd Mon Sep 17 00:00:00 2001 From: Roland Dufour Date: Mon, 6 Nov 2023 17:51:48 +0100 Subject: [PATCH] feature #5948 Add mode to search all/any terms (all as default) --- doc/crud.rst | 16 ++++----- src/Dto/CrudDto.php | 2 +- src/Dto/SearchDto.php | 2 +- ...p => AnyTermsCrudSearchControllerTest.php} | 34 +++++++++--------- .../Search/CustomCrudSearchControllerTest.php | 29 ++++++++------- .../DefaultCrudSearchControllerTest.php | 36 ++++++++++++++----- tests/Dto/SearchDtoTest.php | 18 ++++++---- ...r.php => AnyTermsCrudSearchController.php} | 6 ++-- .../Search/CustomCrudSearchController.php | 2 +- 9 files changed, 84 insertions(+), 61 deletions(-) rename tests/Controller/Search/{AllTermsCrudSearchControllerTest.php => AnyTermsCrudSearchControllerTest.php} (66%) rename tests/TestApplication/src/Controller/Search/{AllTermsCrudSearchController.php => AnyTermsCrudSearchController.php} (74%) diff --git a/doc/crud.rst b/doc/crud.rst index 360c460386..49e7f84446 100644 --- a/doc/crud.rst +++ b/doc/crud.rst @@ -240,21 +240,21 @@ Search, Order, and Pagination Options ->setSearchFields(null) // call this method to focus the search input automatically when loading the 'index' page ->setAutofocusSearch() - // match any terms (default mode) - // term1 in (field1 or field2) or term2 in (field1 or field2) - ->setSearchMode(SearchMode::ANY_TERMS) - // force to match all the terms + // force to match all the terms (default mode) // term1 in (field1 or field2) and term2 in (field1 or field2) ->setSearchMode(SearchMode::ALL_TERMS) + // match any terms + // term1 in (field1 or field2) or term2 in (field1 or field2) + ->setSearchMode(SearchMode::ANY_TERMS) ; } .. tip:: - The search engine makes an OR query by default (searching for ``foo bar`` - returns items with ``foo`` OR ``bar`` OR ``foo bar``). You can wrap all or - part of your query with quotes to make an exact search: ``"foo bar"`` only - returns items with that exact content, including the middle white space. + The search engine splits all terms by default (searching for ``foo bar`` + returns items with ``foo`` and ``bar``). You can wrap all or part of your + query with quotes to make an exact search: ``"foo bar"`` only returns + items with that exact content, including the middle white space. :: diff --git a/src/Dto/CrudDto.php b/src/Dto/CrudDto.php index e1de010bc2..5a0fd25939 100644 --- a/src/Dto/CrudDto.php +++ b/src/Dto/CrudDto.php @@ -52,7 +52,7 @@ final class CrudDto private ?string $decimalSeparator = null; private array $defaultSort = []; private ?array $searchFields = []; - private string $searchMode = SearchMode::ANY_TERMS; + private string $searchMode = SearchMode::ALL_TERMS; private bool $autofocusSearch = false; private bool $showEntityActionsAsDropdown = true; private ?PaginatorDto $paginatorDto = null; diff --git a/src/Dto/SearchDto.php b/src/Dto/SearchDto.php index d0cbc2dd50..cd12291e41 100644 --- a/src/Dto/SearchDto.php +++ b/src/Dto/SearchDto.php @@ -22,7 +22,7 @@ final class SearchDto private ?array $appliedFilters; private string $searchMode; - public function __construct(Request $request, ?array $searchableProperties, ?string $query, array $defaultSort, array $customSort, ?array $appliedFilters, string $searchMode = SearchMode::ANY_TERMS) + public function __construct(Request $request, ?array $searchableProperties, ?string $query, array $defaultSort, array $customSort, ?array $appliedFilters, string $searchMode = SearchMode::ALL_TERMS) { $this->request = $request; $this->searchableProperties = $searchableProperties; diff --git a/tests/Controller/Search/AllTermsCrudSearchControllerTest.php b/tests/Controller/Search/AnyTermsCrudSearchControllerTest.php similarity index 66% rename from tests/Controller/Search/AllTermsCrudSearchControllerTest.php rename to tests/Controller/Search/AnyTermsCrudSearchControllerTest.php index 99bc8c940b..8096b5ff8a 100644 --- a/tests/Controller/Search/AllTermsCrudSearchControllerTest.php +++ b/tests/Controller/Search/AnyTermsCrudSearchControllerTest.php @@ -4,13 +4,13 @@ use EasyCorp\Bundle\EasyAdminBundle\Test\AbstractCrudTestCase; use EasyCorp\Bundle\EasyAdminBundle\Tests\TestApplication\Controller\DashboardController; -use EasyCorp\Bundle\EasyAdminBundle\Tests\TestApplication\Controller\Search\AllTermsCrudSearchController; +use EasyCorp\Bundle\EasyAdminBundle\Tests\TestApplication\Controller\Search\AnyTermsCrudSearchController; -class AllTermsCrudSearchControllerTest extends AbstractCrudTestCase +class AnyTermsCrudSearchControllerTest extends AbstractCrudTestCase { protected function getControllerFqcn(): string { - return AllTermsCrudSearchController::class; + return AnyTermsCrudSearchController::class; } protected function getDashboardFqcn(): string @@ -41,29 +41,29 @@ public static function provideSearchTests(): iterable $numOfPostsWrittenByEachAuthor = 4; $numOfPostsPublishedByEachUser = 2; - yield 'search by blog post title and author or publisher email no results' => [ - '"Blog Post 10" "user4@"', + yield 'search by blog post title yields no results' => [ + 'blog post', 0, ]; - yield 'search by blog post title and author or publisher email' => [ - 'Blog Post "user4@"', - $numOfPostsWrittenByEachAuthor + $numOfPostsPublishedByEachUser, + yield 'search by blog post slug yields no results' => [ + 'blog-post', + 0, ]; - yield 'search by author and publisher email' => [ - 'user1 user2@', - $numOfPostsPublishedByEachUser, + yield 'search by author or publisher email' => [ + '@example.com', + $totalNumberOfPosts, ]; - yield 'search by author and publisher email no results' => [ - 'user1 user3@', - 0, + yield 'quoted search by author or published email' => [ + '"user4@"', + $numOfPostsWrittenByEachAuthor + $numOfPostsPublishedByEachUser, ]; - yield 'search by author or publisher email' => [ - 'user4', - $numOfPostsWrittenByEachAuthor + $numOfPostsPublishedByEachUser, + yield 'multiple search by author or publisher email (partial or complete)' => [ + '"user2@example.com" "user4@"', + 2 * $numOfPostsWrittenByEachAuthor + 2 * $numOfPostsPublishedByEachUser, ]; } } diff --git a/tests/Controller/Search/CustomCrudSearchControllerTest.php b/tests/Controller/Search/CustomCrudSearchControllerTest.php index 208248fb67..5f97506f86 100644 --- a/tests/Controller/Search/CustomCrudSearchControllerTest.php +++ b/tests/Controller/Search/CustomCrudSearchControllerTest.php @@ -37,33 +37,32 @@ public static function provideSearchTests(): iterable { // the CRUD Controller associated to this test has configured the search // properties used by the search engine. That's why results are not the default ones - $totalNumberOfPosts = 20; $numOfPostsWrittenByEachAuthor = 4; $numOfPostsPublishedByEachUser = 2; - yield 'search by blog post title yields no results' => [ - 'blog post', + yield 'search by blog post title and author or publisher email no results' => [ + '"Blog Post 10" "user4@"', 0, ]; - yield 'search by blog post slug yields no results' => [ - 'blog-post', - 0, + yield 'search by blog post title and author or publisher email' => [ + 'Blog Post "user4@"', + $numOfPostsWrittenByEachAuthor + $numOfPostsPublishedByEachUser, ]; - yield 'search by author or publisher email' => [ - '@example.com', - $totalNumberOfPosts, + yield 'search by author and publisher email' => [ + 'user1 user2@', + $numOfPostsPublishedByEachUser, ]; - yield 'quoted search by author or published email' => [ - '"user4@"', - $numOfPostsWrittenByEachAuthor + $numOfPostsPublishedByEachUser, + yield 'search by author and publisher email no results' => [ + 'user1 user3@', + 0, ]; - yield 'multiple search by author or publisher email (partial or complete)' => [ - '"user2@example.com" "user4@"', - 2 * $numOfPostsWrittenByEachAuthor + 2 * $numOfPostsPublishedByEachUser, + yield 'search by author or publisher email' => [ + 'user4', + $numOfPostsWrittenByEachAuthor + $numOfPostsPublishedByEachUser, ]; } } diff --git a/tests/Controller/Search/DefaultCrudSearchControllerTest.php b/tests/Controller/Search/DefaultCrudSearchControllerTest.php index a3ab943c5f..083645be2a 100644 --- a/tests/Controller/Search/DefaultCrudSearchControllerTest.php +++ b/tests/Controller/Search/DefaultCrudSearchControllerTest.php @@ -123,18 +123,36 @@ public static function provideSearchTests(): iterable $totalNumberOfPosts, ]; - yield 'default search is OR search' => [ + yield 'search all blog posts (use quotes)' => [ [], - 'post 17', + '"blog post"', $totalNumberOfPosts, ]; - yield 'use quotes to make an AND search' => [ + yield 'search all terms' => [ + [], + 'blog post-17', + 1, + ]; + + yield 'search all terms (inversed terms)' => [ + [], + 'post-17 blog', + 1, + ]; + + yield 'search all terms with quotes' => [ [], '"post 17"', 1, ]; + yield 'search all terms with quotes (inversed terms)' => [ + [], + '"17 post"', + 0, + ]; + yield 'quoted terms with inside quotes' => [ [ ['title' => 'Foo "Bar Baz', 'slug' => 'foo-bar-baz'], @@ -143,16 +161,16 @@ public static function provideSearchTests(): iterable 1, ]; - yield "multiple quoted terms (it's an OR of two AND terms)" => [ + yield 'multiple quoted terms' => [ [], - '"post 17" "post 18"', - 2, + '"Blog post" "post 17"', + 1, ]; - yield "multiple quoted terms and unquoted terms (it's an OR search again)" => [ + yield 'multiple quoted terms and unquoted terms' => [ [], - '"post 17" "post 18" post 5', - $totalNumberOfPosts, + '"Blog post" "post 17" post ipsum', + 1, ]; } diff --git a/tests/Dto/SearchDtoTest.php b/tests/Dto/SearchDtoTest.php index 59a387536b..7b957a2778 100644 --- a/tests/Dto/SearchDtoTest.php +++ b/tests/Dto/SearchDtoTest.php @@ -11,13 +11,13 @@ class SearchDtoTest extends TestCase { public function testQueryStringIsTrimmedAutomatically() { - $dto = new SearchDto(new Request(), null, ' foo ', [], [], null, SearchMode::ANY_TERMS); + $dto = new SearchDto(new Request(), null, ' foo ', [], [], null); $this->assertSame('foo', $dto->getQuery()); } public function testDefaultSort() { - $dto = new SearchDto(new Request(), null, null, ['foo' => 'ASC'], [], null, SearchMode::ANY_TERMS); + $dto = new SearchDto(new Request(), null, null, ['foo' => 'ASC'], [], null); $this->assertSame(['foo' => 'ASC'], $dto->getSort()); } @@ -26,7 +26,7 @@ public function testDefaultSort() */ public function testSortConfigMerging(array $defaultSort, array $customSort, array $expectedSortConfig) { - $dto = new SearchDto(new Request(), null, null, $defaultSort, $customSort, null, SearchMode::ANY_TERMS); + $dto = new SearchDto(new Request(), null, null, $defaultSort, $customSort, null); $this->assertSame($expectedSortConfig, $dto->getSort()); } @@ -35,7 +35,7 @@ public function testSortConfigMerging(array $defaultSort, array $customSort, arr */ public function testIsSortingField(array $defaultSort, array $customSort, string $fieldName, bool $expectedResult) { - $dto = new SearchDto(new Request(), null, null, $defaultSort, $customSort, null, SearchMode::ANY_TERMS); + $dto = new SearchDto(new Request(), null, null, $defaultSort, $customSort, null); $this->assertSame($expectedResult, $dto->isSortingField($fieldName)); } @@ -44,7 +44,7 @@ public function testIsSortingField(array $defaultSort, array $customSort, string */ public function testGetSortDirection(array $defaultSort, array $customSort, string $fieldName, string $expectedDirection) { - $dto = new SearchDto(new Request(), null, null, $defaultSort, $customSort, null, SearchMode::ANY_TERMS); + $dto = new SearchDto(new Request(), null, null, $defaultSort, $customSort, null); $this->assertSame($expectedDirection, $dto->getSortDirection($fieldName)); } @@ -53,10 +53,16 @@ public function testGetSortDirection(array $defaultSort, array $customSort, stri */ public function testGetQueryTerms(string $query, array $expectedQueryTerms) { - $dto = new SearchDto(new Request(), null, $query, [], [], null, SearchMode::ANY_TERMS); + $dto = new SearchDto(new Request(), null, $query, [], [], null); $this->assertSame($expectedQueryTerms, $dto->getQueryTerms()); } + public function testDefaultSearchMode() + { + $dto = new SearchDto(new Request(), null, null, ['foo' => 'ASC'], [], null); + $this->assertSame(SearchMode::ALL_TERMS, $dto->getSearchMode()); + } + public function testSearchMode() { foreach ([SearchMode::ANY_TERMS, SearchMode::ALL_TERMS] as $searchMode) { diff --git a/tests/TestApplication/src/Controller/Search/AllTermsCrudSearchController.php b/tests/TestApplication/src/Controller/Search/AnyTermsCrudSearchController.php similarity index 74% rename from tests/TestApplication/src/Controller/Search/AllTermsCrudSearchController.php rename to tests/TestApplication/src/Controller/Search/AnyTermsCrudSearchController.php index 6e8fa98516..e5468d018f 100644 --- a/tests/TestApplication/src/Controller/Search/AllTermsCrudSearchController.php +++ b/tests/TestApplication/src/Controller/Search/AnyTermsCrudSearchController.php @@ -7,7 +7,7 @@ use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController; use EasyCorp\Bundle\EasyAdminBundle\Tests\TestApplication\Entity\BlogPost; -class AllTermsCrudSearchController extends AbstractCrudController +class AnyTermsCrudSearchController extends AbstractCrudController { public static function getEntityFqcn(): string { @@ -17,7 +17,7 @@ public static function getEntityFqcn(): string public function configureCrud(Crud $crud): Crud { return parent::configureCrud($crud) - ->setSearchFields(['title', 'author.email', 'publisher.email']) - ->setSearchMode(SearchMode::ALL_TERMS); + ->setSearchFields(['id', 'author.email', 'publisher.email']) + ->setSearchMode(SearchMode::ANY_TERMS); } } diff --git a/tests/TestApplication/src/Controller/Search/CustomCrudSearchController.php b/tests/TestApplication/src/Controller/Search/CustomCrudSearchController.php index cfe3d39eef..b193f1e23d 100644 --- a/tests/TestApplication/src/Controller/Search/CustomCrudSearchController.php +++ b/tests/TestApplication/src/Controller/Search/CustomCrudSearchController.php @@ -16,6 +16,6 @@ public static function getEntityFqcn(): string public function configureCrud(Crud $crud): Crud { return parent::configureCrud($crud) - ->setSearchFields(['id', 'author.email', 'publisher.email']); + ->setSearchFields(['title', 'author.email', 'publisher.email']); } }