diff --git a/app/Facade/Filters/Filter.php b/app/Facade/Filters/Filter.php new file mode 100644 index 0000000..ffb88f2 --- /dev/null +++ b/app/Facade/Filters/Filter.php @@ -0,0 +1,10 @@ + [ 'required', 'string' ], + // 'filters.*.type' => [ 'required', Rule::enum(FilterTypeEnum::class) ], + // 'filters.*.filter' => [ 'required', 'string' ], + + // 'orders.*.by' => [ 'required', 'string' ], + // 'orders.*.sort' => [ 'required', Rule::enum(OrderTypeEnum::class) ], + + // 'pagination.size' => 'number', + // 'pagination.page' => 'number', + // ]; + + /** + * @var Filter[] + */ + public array $filters; + + /** + * @var Order[] + */ + public array $orders; + + public ?Pagination $pagination; + + public static function fromArrayOrObject(array | object $data): Filters + { + $mapper = new JsonMapper(); + if (is_array($data)) { + $data = json_decode(json_encode($data), false); + } + + $data->filters ??= []; + $data->orders ??= []; + $data->pagination ??= null; + + return $mapper->map($data, \App\Facade\Filters\Filters::class); + } + + public function apply(Builder $builder) + { + foreach ($this->filters as $filter) { + switch ($filter->type) { + case FilterTypeEnum::Is: + $builder->where($filter->column, $filter->filter); + break; + case FilterTypeEnum::Like: + $builder->whereLike($filter->column, $filter->filter); + break; + } + } + + foreach ($this->orders as $order) { + $builder->orderBy($order->by, $order->sort->value); + } + + if (!is_null($this->pagination)) { + $builder->paginate($this->pagination->size , ['*'], 'page', $this->pagination->page); + } + } +} \ No newline at end of file diff --git a/app/Facade/Filters/Order.php b/app/Facade/Filters/Order.php new file mode 100644 index 0000000..f28a742 --- /dev/null +++ b/app/Facade/Filters/Order.php @@ -0,0 +1,9 @@ +userService->listAll(); } + public function listFilters(AuthorizedRequest $request) + { + $filters = Filters::fromArrayOrObject($request->all()); + return $this->userService->listAll($filters); + } + public function get(AuthorizedRequest $request, string $id) { return $this->userService->getOneById($id); diff --git a/app/Http/Requests/AuthorizedFilterListRequest.php b/app/Http/Requests/AuthorizedFilterListRequest.php new file mode 100644 index 0000000..5a64a64 --- /dev/null +++ b/app/Http/Requests/AuthorizedFilterListRequest.php @@ -0,0 +1,12 @@ +save(); } - public function listAll(): array + public function listAll(?Filters $filters = null): array { - return User::all()->toArray(); + if (is_null($filters)) { + return User::all()->toArray(); + } + $builder = User::query(); + $filters->apply($builder); + return $builder->get()->toArray(); } public function getOneById(string $id): User | null diff --git a/composer.json b/composer.json index 140e837..33f6b4d 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,8 @@ "bjeavons/zxcvbn-php": "^1.3", "egulias/email-validator": "^4.0", "laravel/framework": "^11.9", - "laravel/tinker": "^2.9" + "laravel/tinker": "^2.9", + "netresearch/jsonmapper": "^4.4" }, "require-dev": { "fakerphp/faker": "^1.23", diff --git a/composer.lock b/composer.lock index cdfcdcb..36b0a0f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "847793c6725e2d2fecbf33402e6d4c4d", + "content-hash": "029c945e578ceadcd46c7f20984f1cd4", "packages": [ { "name": "bjeavons/zxcvbn-php", @@ -2079,6 +2079,57 @@ ], "time": "2024-08-19T06:22:39+00:00" }, + { + "name": "netresearch/jsonmapper", + "version": "v4.4.1", + "source": { + "type": "git", + "url": "https://github.com/cweiske/jsonmapper.git", + "reference": "132c75c7dd83e45353ebb9c6c9f591952995bbf0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/132c75c7dd83e45353ebb9c6c9f591952995bbf0", + "reference": "132c75c7dd83e45353ebb9c6c9f591952995bbf0", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0 || ~10.0", + "squizlabs/php_codesniffer": "~3.5" + }, + "type": "library", + "autoload": { + "psr-0": { + "JsonMapper": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "OSL-3.0" + ], + "authors": [ + { + "name": "Christian Weiske", + "email": "cweiske@cweiske.de", + "homepage": "http://github.com/cweiske/jsonmapper/", + "role": "Developer" + } + ], + "description": "Map nested JSON structures onto PHP classes", + "support": { + "email": "cweiske@cweiske.de", + "issues": "https://github.com/cweiske/jsonmapper/issues", + "source": "https://github.com/cweiske/jsonmapper/tree/v4.4.1" + }, + "time": "2024-01-31T06:18:54+00:00" + }, { "name": "nette/schema", "version": "v1.3.0", diff --git a/public/openapi.yml b/public/openapi.yml index 56dee84..a8e742f 100644 --- a/public/openapi.yml +++ b/public/openapi.yml @@ -148,6 +148,30 @@ paths: description: Auth failed 403: description: Auth failed + post: + tags: + - Private routes + security: + - session: [] + summary: List all users with filters + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Filters' + responses: + 200: + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/User' + 401: + description: Auth failed + 403: + description: Auth failed /api/users/private/get/{id}: get: parameters: @@ -424,6 +448,69 @@ components: items: type: string example: 'This field is invalid!' + Filters: + type: object + properties: + filters: + type: array + items: + $ref: '#/components/schemas/Filter' + example: [ + { + "column": "name", + "type": "like", + "filter": "%ad%" + }, + { + "column": "name", + "type": "is", + "filter": "jade" + } + ] + orders: + type: array + items: + $ref: '#/components/schemas/Order' + example: [ + { + "by": "id", + "type": "asc" + }, + { + "by": "id", + "type": "desc" + } + ] + pagination: + type: object + properties: + size: + type: number + example: 100 + page: + type: number + example: 1 + Filter: + type: object + properties: + column: + type: string + example: column_name + type: + type: string + example: 'is' + filter: + type: string + example: '123' + Order: + type: object + properties: + by: + type: string + example: column_name + sort: + type: string + example: 'asc|desc' securitySchemes: session: type: http diff --git a/routes/web.php b/routes/web.php index 3403999..76109a4 100644 --- a/routes/web.php +++ b/routes/web.php @@ -17,6 +17,7 @@ Route::prefix('/api')->group(function() { }); Route::controller(PrivateUserController::class)->prefix('/users/private')->group(function () { Route::get('/list', 'list'); + Route::post('/list', 'listFilters'); Route::get('/get/{id}', 'get')->whereUuid('id'); Route::put('/edit/{id}', 'edit')->whereUuid('id'); });