Created
May 3, 2022 14:34
-
-
Save oddvalue/6b129bf80b72b07419360abeed9b3179 to your computer and use it in GitHub Desktop.
Laravel rest API support for filtering and sorting as defined by https://jsonapi.org/recommendations/#filtering
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| use Illuminate\Database\Eloquent\Builder; | |
| use Illuminate\Database\Eloquent\Model; | |
| use Illuminate\Http\Request; | |
| abstract class ApiSearchQuery | |
| { | |
| /** | |
| * @var string The model class for the query. | |
| */ | |
| protected string $model; | |
| /** | |
| * @var string[] The fields that can be used for sorting | |
| */ | |
| protected array $sortable = []; | |
| /** | |
| * @var string[] The fields that can be used for filtering | |
| */ | |
| protected array $filterable = []; | |
| /** | |
| * @var Request | |
| */ | |
| protected Request $request; | |
| /** | |
| * @var Builder | |
| */ | |
| protected Builder $query; | |
| public function __construct(Request $request) | |
| { | |
| $this->request = $request; | |
| $this->query = $this->getModel()->newQuery(); | |
| } | |
| protected function getModel(): Model | |
| { | |
| return (new $this->model); | |
| } | |
| protected function canSort(string $key): bool | |
| { | |
| return in_array($key, $this->getSortableKeys()); | |
| } | |
| protected function canFilter(string $key): bool | |
| { | |
| return in_array($key, $this->getFilterableKeys()); | |
| } | |
| protected function getSortableKeys(): array | |
| { | |
| return $this->sortable; | |
| } | |
| protected function getFilterableKeys(): array | |
| { | |
| return $this->filterable; | |
| } | |
| public function sort(): static | |
| { | |
| collect(explode(',', $this->request->sort))->map(function ($sort) { | |
| @list($key, $direction) = explode(':', $sort); | |
| return [ | |
| 'key' => $key, | |
| 'direction' => $direction === 'desc' ? 'desc' : 'asc', | |
| ]; | |
| })->filter(function ($sort) { | |
| return $this->canSort($sort['key']); | |
| })->map(function ($sort) { | |
| $this->query->orderBy($sort['key'], $sort['direction']); | |
| }); | |
| return $this; | |
| } | |
| public function filter(): static | |
| { | |
| collect($this->request->filter)->map(function ($filter, $key) { | |
| @list($operationOrValue, $value) = explode(':', $filter); | |
| if ($value) { | |
| $queryOperation = $operationOrValue; | |
| } else { | |
| $queryOperation = null; | |
| $value = $operationOrValue; | |
| } | |
| switch ($queryOperation) { | |
| case 'in': | |
| $operation = 'in'; | |
| $value = explode(',', $value); | |
| break; | |
| case 'nin': | |
| $operation = 'not in'; | |
| $value = explode(',', $value); | |
| break; | |
| case 'neq': | |
| $operation = '!='; | |
| break; | |
| case 'gt': | |
| $operation = '>'; | |
| break; | |
| case 'gte': | |
| $operation = '>='; | |
| break; | |
| case 'lt': | |
| $operation = '<'; | |
| break; | |
| case 'lte': | |
| $operation = '<='; | |
| break; | |
| case 'like': | |
| $operation = 'like'; | |
| break; | |
| default: | |
| $operation = '='; | |
| break; | |
| } | |
| return [ | |
| 'key' => $key, | |
| 'operation' => $operation, | |
| 'value' => $value, | |
| ]; | |
| })->filter(function ($filter) { | |
| return $this->canFilter($filter['key']); | |
| })->map(function ($filter) { | |
| switch ($filter['value']) { | |
| case 'null': | |
| return $this->query->whereNull($filter['key']); | |
| case 'notnull': | |
| return $this->query->whereNotNull($filter['key']); | |
| } | |
| switch ($filter['operation']) { | |
| case 'in': | |
| return $this->query->whereIn($filter['key'], $filter['value']); | |
| case 'not in': | |
| return $this->query->whereNotIn($filter['key'], $filter['value']); | |
| default: | |
| return $this->query->where($filter['key'], $filter['operation'], $filter['value']); | |
| } | |
| }); | |
| return $this; | |
| } | |
| public function __call(string $method, array $params): static | |
| { | |
| $query = call_user_func_array([$this->query, $method], $params); | |
| if (!$query instanceof Builder) { | |
| return $query; | |
| } | |
| return $this; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment