diff --git a/.env.example.complete b/.env.example.complete index a13c8b7d0..716d614a3 100644 --- a/.env.example.complete +++ b/.env.example.complete @@ -258,3 +258,6 @@ ALLOW_CONTENT_SCRIPTS=false # Contents of the robots.txt file can be overridden, making this option obsolete. ALLOW_ROBOTS=null +# The default and maximum item-counts for listing API requests. +API_DEFAULT_ITEM_COUNT=100 +API_MAX_ITEM_COUNT=500 \ No newline at end of file diff --git a/app/Api/ListingResponseBuilder.php b/app/Api/ListingResponseBuilder.php new file mode 100644 index 000000000..279fabd5d --- /dev/null +++ b/app/Api/ListingResponseBuilder.php @@ -0,0 +1,82 @@ +query = $query; + $this->fields = $fields; + } + + /** + * Get the response from this builder. + */ + public function toResponse() + { + $total = $this->query->count(); + $data = $this->fetchData(); + + return response()->json([ + 'data' => $data, + 'total' => $total, + ]); + } + + /** + * Fetch the data to return in the response. + */ + protected function fetchData(): Collection + { + $this->applyCountAndOffset($this->query); + $this->applySorting($this->query); + // TODO - Apply filtering + + return $this->query->get($this->fields); + } + + /** + * Apply sorting operations to the query from given parameters + * otherwise falling back to the first given field, ascending. + */ + protected function applySorting(Builder $query) + { + $defaultSortName = $this->fields[0]; + $direction = 'asc'; + + $sort = request()->get('sort', ''); + if (strpos($sort, '-') === 0) { + $direction = 'desc'; + } + + $sortName = ltrim($sort, '+- '); + if (!in_array($sortName, $this->fields)) { + $sortName = $defaultSortName; + } + + $query->orderBy($sortName, $direction); + } + + /** + * Apply count and offset for paging, based on params from the request while falling + * back to system defined default, taking the max limit into account. + */ + protected function applyCountAndOffset(Builder $query) + { + $offset = max(0, request()->get('offset', 0)); + $maxCount = config('api.max_item_count'); + $count = request()->get('count', config('api.default_item_count')); + $count = max(min($maxCount, $count), 1); + + $query->skip($offset)->take($count); + } +} diff --git a/app/Config/api.php b/app/Config/api.php new file mode 100644 index 000000000..dd54b2906 --- /dev/null +++ b/app/Config/api.php @@ -0,0 +1,20 @@ + env('API_DEFAULT_ITEM_COUNT', 100), + + // The maximum number of items that can be returned in a listing API request. + 'max_item_count' => env('API_MAX_ITEM_COUNT', 500), + +]; diff --git a/app/Http/Controllers/Api/ApiController.php b/app/Http/Controllers/Api/ApiController.php new file mode 100644 index 000000000..4971c0cde --- /dev/null +++ b/app/Http/Controllers/Api/ApiController.php @@ -0,0 +1,20 @@ +toResponse(); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Api/BooksApiController.php b/app/Http/Controllers/Api/BooksApiController.php new file mode 100644 index 000000000..d23aaf025 --- /dev/null +++ b/app/Http/Controllers/Api/BooksApiController.php @@ -0,0 +1,18 @@ +apiListingResponse($books, [ + 'id', 'name', 'slug', 'description', 'created_at', 'updated_at', 'created_by', 'updated_by', + 'restricted', 'image_id', + ]); + } +} \ No newline at end of file diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index f9752da09..cd3fc83ec 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -37,7 +37,6 @@ class Kernel extends HttpKernel ], 'api' => [ 'throttle:60,1', - 'bindings', ], ]; diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index c4c39d534..a37780e52 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -34,7 +34,7 @@ class RouteServiceProvider extends ServiceProvider public function map() { $this->mapWebRoutes(); -// $this->mapApiRoutes(); + $this->mapApiRoutes(); } /** * Define the "web" routes for the application. @@ -63,7 +63,7 @@ class RouteServiceProvider extends ServiceProvider { Route::group([ 'middleware' => 'api', - 'namespace' => $this->namespace, + 'namespace' => $this->namespace . '\Api', 'prefix' => 'api', ], function ($router) { require base_path('routes/api.php'); diff --git a/routes/api.php b/routes/api.php new file mode 100644 index 000000000..0604ffd29 --- /dev/null +++ b/routes/api.php @@ -0,0 +1,12 @@ +