From 6bcdc3a34fe2dfb3c4e4e1575545cd81f3e82361 Mon Sep 17 00:00:00 2001 From: Ryan Bibby Date: Mon, 16 Jan 2023 12:52:26 +0000 Subject: [PATCH 1/2] Allow multiple sorts to work when sort order doesn't match config order --- src/Sieve.php | 83 +++++++++++++++++++++++++++------------------ tests/SieveTest.php | 42 +++++++++++++++++++++-- 2 files changed, 89 insertions(+), 36 deletions(-) diff --git a/src/Sieve.php b/src/Sieve.php index b3d8827..eb5c241 100644 --- a/src/Sieve.php +++ b/src/Sieve.php @@ -12,8 +12,6 @@ class Sieve protected $defaultSort = null; - protected $sortable = []; - public function __construct(Request $request) { $this->request = $request; @@ -57,8 +55,41 @@ public function apply($queryBuilder) $search = new SearchTerm($property, $operator, $property, $term); $filter->modifyQuery($queryBuilder, $search); } + } + + if ($this->request->has('sort')) { + $this->applyRequestSorts($queryBuilder); + } elseif ($this->defaultSort) { + $this->applyDefaultSort($queryBuilder); + } + + return $this; + } + + public function getSort(): ?string + { + return $this->request->get("sort") ?? $this->defaultSort; + } + + public function setDefaultSort($property = 'id', $direction = 'asc'): Sieve + { + $this->defaultSort = $property . ':' . $direction; + + return $this; + } + + protected function applyRequestSorts($queryBuilder): void { + $sorts = explode(',', $this->getSort()); + foreach ($sorts as $sort) { + $property = explode(':', $sort)[0]; + + $filterRule = collect($this->getFilters())->firstWhere('property', $property); + if (!$filterRule) { + continue; + } $column = $property; + $filter = $filterRule['filter']; while ($filter instanceof WrapsFilter) { if ($filter instanceof MapFilter) { $column = $filter->target(); @@ -68,47 +99,33 @@ public function apply($queryBuilder) $filter = $filter->getWrapped(); } - if (strpos($column, ".") !== false) { + if (str_contains($column, ".")) { continue; } - $sorts = explode(',', $this->getSort()); - foreach ($sorts as $sort) { - if ($sort == "$property:desc") { - $queryBuilder->orderBy($column, "desc"); - } + if ($sort == "$property:desc") { + $queryBuilder->orderBy($column, "desc"); + } - if ($sort == "$property:asc" || $sort == $property) { - $queryBuilder->orderBy($column, "asc"); - } + if ($sort == "$property:asc" || $sort == $property) { + $queryBuilder->orderBy($column, "asc"); + } - if ($sort == "$property:asc_nulls_last") { - $queryBuilder->orderByRaw("ISNULL($column) asc") - ->orderBy($column, 'asc'); - } + if ($sort == "$property:asc_nulls_last") { + $queryBuilder->orderByRaw("ISNULL($column) asc") + ->orderBy($column, 'asc'); + } - if ($sort == "$property:desc_nulls_first") { - $queryBuilder->orderByRaw("ISNULL($column) desc") - ->orderBy($column, 'desc'); - } + if ($sort == "$property:desc_nulls_first") { + $queryBuilder->orderByRaw("ISNULL($column) desc") + ->orderBy($column, 'desc'); } } - - return $this; } - public function getSort(): ?string - { - return $this->request->get("sort") ?? $this->defaultSort; - } - - public function setDefaultSort($property = 'id', $direction = 'asc'): Sieve + protected function applyDefaultSort($queryBuilder): void { - $this->sortable[] = $property; - $this->defaultSort = $property . ':' . $direction; - - return $this; + list($column, $direction) = explode(':', $this->defaultSort); + $queryBuilder->orderBy($column, $direction); } - - } diff --git a/tests/SieveTest.php b/tests/SieveTest.php index 0b23a59..ac2963c 100644 --- a/tests/SieveTest.php +++ b/tests/SieveTest.php @@ -20,8 +20,8 @@ public function filters_and_sorts() 'sort' => 'name:desc' ]); - $seive = new Sieve($request); - $seive->configure(fn ($builder) => [ + $sieve = new Sieve($request); + $sieve->configure(fn ($builder) => [ 'name' => $builder->string(), ]); @@ -29,7 +29,7 @@ public function filters_and_sorts() $builder = $this->app->make(Builder::class); $builder->from('pets'); - $seive->apply($builder); + $sieve->apply($builder); $this->assertEquals( 'select * from "pets" where "name" in (?, ?) order by "name" desc', @@ -48,6 +48,17 @@ public function set_default_sort_filter() $sieve->setDefaultSort('name', 'desc'); $this->assertEquals($sieve->getSort(), 'name:desc'); + + /** @var Builder */ + $builder = $this->app->make(Builder::class); + $builder->from('pets'); + + $sieve->apply($builder); + + $this->assertEquals( + 'select * from "pets" order by "name" desc', + $builder->toSql() + ); } /** @@ -196,6 +207,31 @@ public function allows_multiple_columns_to_be_ordered_including_a_null_column() ); } + /** + * @test + */ + public function applies_order_by_in_order_when_sieve_config_order_is_different() + { + $request = Request::create('/', 'GET', [ + 'sort' => 'type:asc,name:desc', + ]); + + $seive = new Sieve($request); + $seive->addFilter('name', new StringFilter); + $seive->addFilter('type', new StringFilter); + + /** @var Builder */ + $builder = $this->app->make(Builder::class); + $builder->from('pets'); + + $seive->apply($builder); + + $this->assertEquals( + 'select * from "pets" order by "type" asc, "name" desc', + $builder->toSql() + ); + } + /** * @test */ From 706fb91f5689c9ce8d1cfafb210e7d6cda90114f Mon Sep 17 00:00:00 2001 From: Ryan Bibby Date: Mon, 16 Jan 2023 13:02:28 +0000 Subject: [PATCH 2/2] Update readme with info on default sort --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fac95d8..36a16f9 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,8 @@ of the column this may not be the desired functionality. You can change this usi * `sort=priority:desc_nulls_first` -You can set a default sort using the `setDefaultSort` on the`Sieve` class. +You can set a default sort using the `setDefaultSort` on the`Sieve` class. The default sort uses the database column +name rather than the mapped property name, meaning you can use the default sort property without having a map set up. ```php $sieve->setDefaultSort('name', 'asc')