From 26ddbad3f33eef57d316d0157e811ea48b3657d5 Mon Sep 17 00:00:00 2001 From: Spitfire Date: Thu, 6 Feb 2025 09:38:08 -0600 Subject: [PATCH] Implemented requested changes --- app/Datagrids/Filters/DatagridFilter.php | 2 +- app/Models/Concerns/HasFilters.php | 101 +++++++++++------------ app/Services/FilterService.php | 15 ++++ resources/views/filters/form.blade.php | 6 +- 4 files changed, 66 insertions(+), 58 deletions(-) diff --git a/app/Datagrids/Filters/DatagridFilter.php b/app/Datagrids/Filters/DatagridFilter.php index 2cc75a506..da212ff86 100644 --- a/app/Datagrids/Filters/DatagridFilter.php +++ b/app/Datagrids/Filters/DatagridFilter.php @@ -224,7 +224,7 @@ protected function tags(): self } $this->filters[] = [ 'field' => 'tags', - 'label' => Module::singular(config('entities.ids.tag'), __('entities.tags')), + 'label' => Module::plural(config('entities.ids.tag'), __('entities.tags')), 'type' => 'tag', 'route' => route('search-list', [$this->campaign, config('entities.ids.tag')]), 'placeholder' => $placeholder, diff --git a/app/Models/Concerns/HasFilters.php b/app/Models/Concerns/HasFilters.php index 67f9e7382..cc557df71 100644 --- a/app/Models/Concerns/HasFilters.php +++ b/app/Models/Concerns/HasFilters.php @@ -8,6 +8,7 @@ use Illuminate\Support\Str; use App\Models\Location; use App\Models\Family; +use App\Models\Organisation; use App\Models\Race; use Illuminate\Support\Facades\DB; @@ -86,7 +87,7 @@ public function scopeFilter(Builder $query, array $params = []): Builder foreach ($this->filterParams as $key => $value) { if (isset($value) && in_array($key, $fields)) { // The requested field is an array, which we don't support for anything other than tags, and locations ("or" searches) - if (is_array($value) && $key != "tags" && $key != "locations" && $key != "organisations" && $key != "races" && $key != "families") { + if (is_array($value) && !in_array($key, ['tags', 'locations', 'organisations', 'races', 'families'])) { continue; } $this->filterOption = !empty($params[$key . '_option']) ? $params[$key . '_option'] : null; @@ -113,7 +114,7 @@ public function scopeFilter(Builder $query, array $params = []): Builder if ($key === 'tags') { $this->filterTags($query, $value); } elseif ($key == 'locations') { - $this->filterMultipleLocations($query, $value); + $this->filterLocations($query, $value); } elseif ($key == 'organisations') { $this->filterOrganisations($query, $value); } elseif ($key == 'races') { @@ -125,7 +126,7 @@ public function scopeFilter(Builder $query, array $params = []): Builder } elseif ($key == 'races') { $this->filterRaces($query, $value); } elseif ($key == 'location_id') { - $this->filterLocations($query, $value, $key); + $this->filterLocation($query, $value, $key); } elseif ($key == 'tag_id') { $query ->joinEntity() @@ -188,7 +189,7 @@ protected function extractSearchOperator($value, string $key): void { $operator = 'like'; $filterValue = $value; - if (($key !== 'tags') && ($key !== 'races') && ($key !== 'locations') && ($key !== 'families') && ($key !== 'organisations')) { + if (!in_array($key, ['tags', 'locations', 'organisations', 'races', 'families'])) { if ($value == '!!') { $operator = 'IS NULL'; $filterValue = null; @@ -496,20 +497,14 @@ protected function filterRaces(Builder $query, null|string|array $value = null): } if ($this->filterOption('children')) { - $races = DB::select(" - WITH RECURSIVE race_tree AS ( - -- Select parents - SELECT * FROM races WHERE id IN (" . implode(',', $raceIds) . ") - UNION ALL - -- Recursively get all descendants - SELECT r.* FROM races r - INNER JOIN race_tree rt ON r.race_id = rt.id - ) - SELECT * FROM race_tree - "); - $raceIds = collect($races)->pluck('id'); // Extract IDs - $query->whereIn($this->getTable() . '.race_id', $raceIds)->distinct(); - return; + $ids = []; + $parents = Race::whereIn('id', $raceIds)->with('descendants')->get(); + foreach ($parents as $parent) { + $childIds = $parent->descendants->pluck('id')->toArray(); + array_push($childIds, $parent->id); + $ids = $childIds + $ids; + } + $value = $ids; } foreach ($value as $v) { @@ -522,9 +517,9 @@ protected function filterRaces(Builder $query, null|string|array $value = null): } /** - * Filter on characters on multiple locations + * Filter characters in location */ - protected function filterLocations(Builder $query, string $value = null, string $key = null): void + protected function filterLocation(Builder $query, string $value = null, string $key = null): void { if (method_exists($this, 'scopeLocation')) { $query->location($value, $this->getFilterOption()); @@ -548,7 +543,7 @@ protected function filterLocations(Builder $query, string $value = null, string /** * Filter on characters on multiple locations */ - protected function filterMultipleLocations(Builder $query, null|string|array $value = null, string $key = null): void + protected function filterLocations(Builder $query, null|string|array $value = null, string $key = null): void { // "none" filter keys is handled later if ($this->filterOption('none')) { @@ -573,20 +568,14 @@ protected function filterMultipleLocations(Builder $query, null|string|array $va } if ($this->filterOption('children')) { - $locations = DB::select(" - WITH RECURSIVE location_tree AS ( - -- Select parents - SELECT * FROM locations WHERE id IN (" . implode(',', $locationIds) . ") - UNION ALL - -- Recursively get all descendants - SELECT l.* FROM locations l - INNER JOIN location_tree lt ON l.location_id = lt.id - ) - SELECT * FROM location_tree - "); - $locIds = collect($locations)->pluck('id'); // Extract IDs - $query->whereIn($this->getTable() . '.location_id', $locIds)->distinct(); - return; + $ids = []; + $parents = Location::whereIn('id', $locationIds)->with('descendants')->get(); + foreach ($parents as $parent) { + $childIds = $parent->descendants->pluck('id')->toArray(); + array_push($childIds, $parent->id); + $ids = $childIds + $ids; + } + $locationIds = $ids; } $query->whereIn($this->getTable() . '.location_id', $locationIds)->distinct(); @@ -621,20 +610,14 @@ protected function filterOrganisations(Builder $query, null|string|array $value } if ($this->filterOption('children')) { - $orgs = DB::select(" - WITH RECURSIVE organisation_tree AS ( - -- Select parents - SELECT * FROM organisations WHERE id IN (" . implode(',', $orgIds) . ") - UNION ALL - -- Recursively get all descendants - SELECT r.* FROM organisations r - INNER JOIN organisation_tree rt ON r.organisation_id = rt.id - ) - SELECT * FROM organisation_tree - "); - $orgIds = collect($orgs)->pluck('id'); // Extract IDs - $query->whereIn($this->getTable() . '.organisation_id', $orgIds)->distinct(); - return; + $ids = []; + $parents = Organisation::whereIn('id', $orgIds)->with('descendants')->get(); + foreach ($parents as $parent) { + $childIds = $parent->descendants->pluck('id')->toArray(); + array_push($childIds, $parent->id); + $ids = $childIds + $ids; + } + $value = $ids; } foreach ($value as $v) { @@ -664,16 +647,28 @@ protected function filterFamilies(Builder $query, null|string|array $value = nul $value = [$value]; } + $familyIds = []; + foreach ($value as $v) { + $familyIds[] = (int) $v; + } + if ($this->filterOption('exclude')) { - $familyIds = []; - foreach ($value as $v) { - $familyIds[] = (int) $v; - } $query->whereRaw('(select count(*) from character_family as cr where cr.character_id = ' . $this->getTable() . '.id and cr.family_id in (' . implode(', ', $familyIds) . ')) = 0'); return; } + if ($this->filterOption('children')) { + $ids = []; + $parents = Family::whereIn('id', $familyIds)->with('descendants')->get(); + foreach ($parents as $parent) { + $childIds = $parent->descendants->pluck('id')->toArray(); + array_push($childIds, $parent->id); + $ids = $childIds + $ids; + } + $value = $ids; + } + foreach ($value as $v) { $v = (int) $v; $query diff --git a/app/Services/FilterService.php b/app/Services/FilterService.php index 47708daba..06797c24e 100644 --- a/app/Services/FilterService.php +++ b/app/Services/FilterService.php @@ -128,6 +128,10 @@ protected function prepareFilters(array $availableFilters = []): self $this->filters = []; } + foreach (['tags', 'locations', 'organisations', 'races', 'families'] as $key) { + $this->checkFilterData($key, $availableFilters); + } + // If we have data but no "tags" array, it's empty if (!empty($this->data) && in_array('tags', $availableFilters) && !isset($this->data['tags'])) { // Not calling from a page or order result, we can junk the filters @@ -246,6 +250,17 @@ protected function prepareOrder(array $availableFields = []): self return $this; } + private function checkFilterData(string $key, array $availableFilters): void + { + // If we have data but no array, it's empty + if (!empty($this->data) && in_array($key, $availableFilters) && !isset($this->data[$key])) { + // Not calling from a page or order result, we can junk the filters + if (empty($this->data['page']) && empty($this->data['order'])) { + unset($this->data[$key]); + } + } + } + /** * @return $this */ diff --git a/resources/views/filters/form.blade.php b/resources/views/filters/form.blade.php index 113fb5632..bf72ff2b8 100644 --- a/resources/views/filters/form.blade.php +++ b/resources/views/filters/form.blade.php @@ -24,20 +24,18 @@ @if (is_array($field)) single($field['field']); if (!empty($value) && $field['type'] == 'select2') { $modelclass = new $field['model']; $model = $modelclass->find($value); } // Support for fields with multiple models selected - if (Illuminate\Support\Arr::get($field, 'multiple') === true && $field['type'] == 'selectMultiple') { + if (Arr::get($field, 'multiple') === true && $field['type'] == 'selectMultiple') { $value = $filterService->filterValue($field['field']); if (!empty($value)) { $modelclass = new $field['model']; $models = $modelclass->whereIn('id', $value)->get()->pluck('name', 'id')->toArray(); - } else { - unset($models); } } ?>