feat: filters for /api/users/private/list

This commit is contained in:
b1ek 2024-08-30 17:14:48 +10:00
parent 27e21f0ad0
commit ca1f629f05
Signed by: blek
GPG Key ID: 14546221E3595D0C
14 changed files with 286 additions and 6 deletions

View File

@ -0,0 +1,10 @@
<?php
namespace App\Facade\Filters;
class Filter
{
public FilterTypeEnum $type;
public string $column;
public string $filter;
}

View File

@ -0,0 +1,9 @@
<?php
namespace App\Facade\Filters;
enum FilterTypeEnum: string
{
case Is = 'is';
case Like = 'like';
}

View File

@ -0,0 +1,71 @@
<?php
namespace App\Facade\Filters;
use App\Facade\Filters\Pagination;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Validation\Rule;
use JsonMapper;
class Filters
{
public const FILTERS_VALIDATION = [];
// 'filters.*.column' => [ '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);
}
}
}

View File

@ -0,0 +1,9 @@
<?php
namespace App\Facade\Filters;
class Order
{
public string $by;
public OrderTypeEnum $sort;
}

View File

@ -0,0 +1,9 @@
<?php
namespace App\Facade\Filters;
enum OrderTypeEnum: string
{
case Asc = 'asc';
case Desc = 'desc';
}

View File

@ -0,0 +1,9 @@
<?php
namespace App\Facade\Filters;
class Pagination
{
public int $size;
public int $page;
}

View File

@ -2,6 +2,7 @@
namespace App\Http\Controllers;
use App\Facade\Filters\Filters;
use App\Http\Requests\AuthorizedRequest;
use App\Http\Requests\UserEditRequest;
use App\Services\UserService;
@ -17,6 +18,12 @@ class PrivateUserController extends Controller
return $this->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);

View File

@ -0,0 +1,12 @@
<?php
namespace App\Http\Requests;
use App\Facade\Filters\Filters;
class AuthorizedFilterListRequest extends AuthorizedRequest
{
public function rules()
{
return Filters::FILTERS_VALIDATION;
}
}

View File

@ -2,8 +2,6 @@
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class AuthorizedRequest extends RestRequest
{
/**

View File

@ -2,6 +2,7 @@
namespace App\Services;
use App\Facade\Filters\Filters;
use App\Models\User;
use Illuminate\Http\Exceptions\HttpResponseException;
use Illuminate\Support\Facades\Auth;
@ -54,10 +55,15 @@ class UserService
$user->save();
}
public function listAll(): array
public function listAll(?Filters $filters = null): array
{
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
{

View File

@ -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",

53
composer.lock generated
View File

@ -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",

View File

@ -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

View File

@ -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');
});