A variable pagination

This commit is contained in:
Fabian Wagner 2026-05-17 12:48:44 +02:00
parent 8bb3e6499a
commit c15d1f9475
2 changed files with 95 additions and 0 deletions

View File

@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace Blax\Workkit\Attributes;
use Attribute;
/**
* Declares per-method pagination policy for a controller action.
*
* `Request::perPage()` reads this attribute via reflection on the resolved
* route action and produces the page size to hand to `->paginate()`:
*
* #[VariablePaginatable] → 25, user can override (1..100)
* #[VariablePaginatable(50)] → 50, user can override (1..100)
* #[VariablePaginatable(10, allowUserOverride: false)] → fixed at 10, no `?per_page=`
* #[VariablePaginatable(50, max: 200)] → 50, user can override (1..200)
*
* Without the attribute, `Request::perPage()` falls back to its $fallback
* argument (15 by default matches Eloquent's model default).
*
* Usage:
*
* #[VariablePaginatable(50)]
* public function index(Request $request): array
* {
* return ResponseService::apiPaginated(
* Book::query()->paginate($request->perPage()),
* BookResource::class,
* );
* }
*/
#[Attribute(Attribute::TARGET_METHOD)]
final class VariablePaginatable
{
public function __construct(
public int $default = 25,
public bool $allowUserOverride = true,
public int $max = 100,
) {
}
}

View File

@ -2,10 +2,14 @@
namespace Blax\Workkit;
use Blax\Workkit\Attributes\VariablePaginatable;
use Blax\Workkit\Commands\Database\BackupCommand;
use Blax\Workkit\Commands\Database\PruneBackupsCommand;
use Blax\Workkit\Commands\Database\RestoreCommand;
use Blax\Workkit\Commands\PlugNPrayCommand;
use Illuminate\Http\Request;
use ReflectionException;
use ReflectionMethod;
class WorkkitServiceProvider extends \Illuminate\Support\ServiceProvider
{
@ -26,6 +30,8 @@ class WorkkitServiceProvider extends \Illuminate\Support\ServiceProvider
*/
public function boot()
{
$this->registerPerPageMacro();
if ($this->app->runningInConsole()) {
$this->commands([
PlugNPrayCommand::class,
@ -41,4 +47,50 @@ class WorkkitServiceProvider extends \Illuminate\Support\ServiceProvider
], 'workkit-config');
}
}
/**
* Resolve the effective page size for the current route via the
* {@see VariablePaginatable} attribute on the controller method.
*
* Order of resolution:
* 1. No route / closure action / no attribute $fallback (default 15)
* 2. Attribute present, allowUserOverride=true clamp `?per_page=N`
* into `[1, max]`, defaulting to `default` when the query is missing.
* 3. Attribute present, allowUserOverride=false `default` (ignores query).
*/
private function registerPerPageMacro(): void
{
Request::macro('perPage', function (int $fallback = 15): int {
/** @var Request $this */
$route = $this->route();
$controller = is_object($route?->getController()) ? $route->getController()::class : null;
$action = is_string($route?->getActionMethod()) ? $route->getActionMethod() : null;
if (! $controller || ! $action) {
return $fallback;
}
try {
$reflection = new ReflectionMethod($controller, $action);
} catch (ReflectionException) {
return $fallback;
}
$attributes = $reflection->getAttributes(VariablePaginatable::class);
if ($attributes === []) {
return $fallback;
}
/** @var VariablePaginatable $config */
$config = $attributes[0]->newInstance();
if (! $config->allowUserOverride) {
return $config->default;
}
$requested = (int) $this->query('per_page', (string) $config->default);
return min(max($requested, 1), $config->max);
});
}
}