diff --git a/src/Infra/Http/Helpers/RequestHelper.php b/src/Infra/Http/Helpers/RequestHelper.php new file mode 100644 index 0000000..5668b7a --- /dev/null +++ b/src/Infra/Http/Helpers/RequestHelper.php @@ -0,0 +1,47 @@ +has('offset') || $request->has('limit')) { + return Pagination::ofOffsetAndLimit( + $request->integer('offset', 0), + $request->integer('limit', $defaultPageSize) + ); + } + + $pageSize = $request->integer( + 'pageSize', + $request->integer('per_page', $request->integer('pagination', $defaultPageSize)) + ); + + return Pagination::ofPage( + $request->integer('page', 1), + $pageSize + ); + } + + public static function toLeagueUri(HttpRequest $request): Http + { + return Http::new($request->url()); + } + + public static function toLeagueFullUri(HttpRequest $request): Http + { + return Http::new($request->fullUrl()); + } +} diff --git a/src/Infra/Providers/AppServiceProvider.php b/src/Infra/Providers/AppServiceProvider.php index 8c2ad28..8dec855 100644 --- a/src/Infra/Providers/AppServiceProvider.php +++ b/src/Infra/Providers/AppServiceProvider.php @@ -4,10 +4,8 @@ declare(strict_types=1); namespace Infra\Providers; -use Carbon\CarbonInterface; use Illuminate\Cache\RateLimiting\Limit; use Illuminate\Database\Eloquent\Model; -use Illuminate\Http\Request as HttpRequest; use Illuminate\Http\Request as IlluminateRequest; use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\Date; @@ -15,6 +13,7 @@ use Illuminate\Support\Facades\RateLimiter; use Illuminate\Support\Facades\Request; use Illuminate\Support\Facades\URL; use Illuminate\Support\ServiceProvider; +use Infra\Http\Helpers\RequestHelper; use League\Uri\Http; use Shared\Pagination; @@ -56,93 +55,30 @@ class AppServiceProvider extends ServiceProvider Date::setLocale(Config::string('app.locale')); Date::setFallbackLocale(Config::string('app.fallback_locale')); - $provider = $this; - - Date::macro('toLocalInput', function (CarbonInterface | \DateTimeInterface | int | float | string | null $date) use ($provider): ?string { - return $provider->toLocalInput($date); + Request::macro('pagination', function (int $defaultPageSize = 25): Pagination { + /** @var \Illuminate\Http\Request $this */ + return RequestHelper::pagination($this, $defaultPageSize); }); - Date::macro('forHumans', function (CarbonInterface | \DateTimeInterface | int | float | string | null $date) use ($provider): ?string { - return $provider->forHumans($date); + Request::macro('toLeagueUri', function (): Http { + /** @var \Illuminate\Http\Request $this */ + return RequestHelper::toLeagueUri($this); }); - Request::macro('pagination', function (int $defaultPageSize = 25) use ($provider): Pagination { - return $provider->pagination(request(), $defaultPageSize); - }); - - Request::macro('toLeagueUri', function () use ($provider): Http { - return $provider->toLeagueUri(request()); - }); - - Request::macro('toLeagueFullUri', function () use ($provider): Http { - return $provider->toLeagueFullUri(request()); + Request::macro('toLeagueFullUri', function (): Http { + /** @var \Illuminate\Http\Request $this */ + return RequestHelper::toLeagueFullUri($this); }); } protected function configureRateLimiting(): void { - RateLimiter::for('web', function (IlluminateRequest $request) { + RateLimiter::for('web', function (IlluminateRequest $_) { return Limit::perMinute(Config::integer('throttles.web'))->by(resolve('clientIp')); }); - RateLimiter::for('api.v1', function (IlluminateRequest $request) { + RateLimiter::for('api.v1', function (IlluminateRequest $_) { return Limit::perMinute(Config::integer('throttles.api.v1'))->by(resolve('clientIp')); }); } - - public function toLocalInput(CarbonInterface | \DateTimeInterface | int | float | string | null $date): ?string - { - if ($date === null) { - return null; - } - - $timezone = request()->string('client_timezone', 'Europe/Paris')->value(); - - return Date::parse($date) - ->setTimezone($timezone) - ->format('Y-m-d\TH:i'); - } - - public function forHumans(CarbonInterface | \DateTimeInterface | int | float | string | null $date): ?string - { - if ($date === null) { - return null; - } - - $timezone = request()->string('client_timezone', 'Europe/Paris')->value(); - - return Date::parse($date) - ->setTimezone($timezone) - ->isoFormat('ddd DD/MM/Y \à HH:mm'); - } - - public function pagination(HttpRequest $request, int $defaultPageSize): Pagination - { - if ($request->has('offset') || $request->has('limit')) { - return Pagination::ofOffsetAndLimit( - $request->integer('offset', 0), - $request->integer('limit', $defaultPageSize) - ); - } - - $pageSize = $request->integer( - 'pageSize', - $request->integer('per_page', $request->integer('pagination', $defaultPageSize)) - ); - - return Pagination::ofPage( - $request->integer('page', 1), - $pageSize - ); - } - - public function toLeagueUri(HttpRequest $request): Http - { - return Http::new($request->url()); - } - - public function toLeagueFullUri(HttpRequest $request): Http - { - return Http::new($request->fullUrl()); - } } diff --git a/tests/Unit/Infra/Http/Helpers/RequestHelperTest.php b/tests/Unit/Infra/Http/Helpers/RequestHelperTest.php new file mode 100644 index 0000000..9c80e39 --- /dev/null +++ b/tests/Unit/Infra/Http/Helpers/RequestHelperTest.php @@ -0,0 +1,116 @@ +getConstructor(); + $constructor?->invoke($reflection->newInstanceWithoutConstructor()); + + // THEN + expect(fn () => $reflection->newInstance()) + ->toThrow(\ReflectionException::class); + }); + + it('creates offset and limit pagination when offset or limit query param is present', function (): void { + // GIVEN + $request = HttpRequest::create('/api/v1/employees?offset=10&limit=50', 'GET'); + + // WHEN + $pagination = RequestHelper::pagination($request, 25); + + // THEN + expect($pagination)->toBeInstanceOf(Pagination::class) + ->and($pagination->from())->toBe(10) + ->and($pagination->to())->toBe(59); + }); + + it('creates page and page size pagination using pageSize query param', function (): void { + // GIVEN + $request = HttpRequest::create('/api/v1/employees?page=2&pageSize=15', 'GET'); + + // WHEN + $pagination = RequestHelper::pagination($request, 25); + + // THEN + expect($pagination)->toBeInstanceOf(Pagination::class) + ->and($pagination->from())->toBe(15) + ->and($pagination->to())->toBe(29); + }); + + it('creates page and page size pagination using per_page query param fallback', function (): void { + // GIVEN + $request = HttpRequest::create('/api/v1/employees?page=3&per_page=20', 'GET'); + + // WHEN + $pagination = RequestHelper::pagination($request, 25); + + // THEN + expect($pagination)->toBeInstanceOf(Pagination::class) + ->and($pagination->from())->toBe(40) + ->and($pagination->to())->toBe(59); + }); + + it('creates page and page size pagination using pagination query param fallback', function (): void { + // GIVEN + $request = HttpRequest::create('/api/v1/employees?page=1&pagination=30', 'GET'); + + // WHEN + $pagination = RequestHelper::pagination($request, 25); + + // THEN + expect($pagination)->toBeInstanceOf(Pagination::class) + ->and($pagination->from())->toBe(0) + ->and($pagination->to())->toBe(29); + }); + + it('uses default page size when no pagination parameters are supplied', function (): void { + // GIVEN + $request = HttpRequest::create('/api/v1/employees', 'GET'); + + // WHEN + $pagination = RequestHelper::pagination($request, 25); + + // THEN + expect($pagination)->toBeInstanceOf(Pagination::class) + ->and($pagination->from())->toBe(0) + ->and($pagination->to())->toBe(24); + }); + + it('converts request URL to League Uri instance', function (): void { + // GIVEN + $request = HttpRequest::create('http://localhost/api/v1/employees?foo=bar', 'GET'); + + // WHEN + $uri = RequestHelper::toLeagueUri($request); + + // THEN + expect($uri)->toBeInstanceOf(Http::class) + ->and((string) $uri)->toBe('http://localhost/api/v1/employees'); + }); + + it('converts full request URL with query parameters to League Uri instance', function (): void { + // GIVEN + $request = HttpRequest::create('http://localhost/api/v1/employees?page=2&pageSize=10', 'GET'); + + // WHEN + $uri = RequestHelper::toLeagueFullUri($request); + + // THEN + expect($uri)->toBeInstanceOf(Http::class) + ->and((string) $uri)->toBe('http://localhost/api/v1/employees?page=2&pageSize=10'); + }); +});