diff --git a/bootstrap/middlewares.php b/bootstrap/middlewares.php
index 20ca07b..90fd85d 100644
--- a/bootstrap/middlewares.php
+++ b/bootstrap/middlewares.php
@@ -3,17 +3,18 @@
declare(strict_types=1);
use Illuminate\Foundation\Configuration\Middleware;
+use Presentation\Api\V1\Http\Middleware\TransformApiV1Response;
return function (Middleware $middleware): void {
$middleware->web(append: [
'throttle:web',
]);
- $middleware->api(append: [
- 'throttle:api.v1',
- ]);
+ $middleware->api(append: []);
- $middleware->alias([
+ $middleware->group('api.v1', [
+ 'throttle:api.v1',
+ TransformApiV1Response::class,
]);
$middleware->preventRequestsDuringMaintenance(except: [
diff --git a/routes/api.php b/routes/api.php
index f2cf552..66cfe54 100644
--- a/routes/api.php
+++ b/routes/api.php
@@ -19,6 +19,9 @@ Route::get(
Route::get('/ping', fn () => response()->json('pong'))->name('api.ping');
// * V1 API
-Route::prefix('v1')->name('api.v1.')->group(function () {
- require __DIR__ . '/apiv1.php';
-});
+Route::prefix('v1')
+ ->name('api.v1.')
+ ->middleware('api.v1')
+ ->group(function (): void {
+ require __DIR__ . '/apiv1.php';
+ });
diff --git a/src/Infra/Usecase/LaravelExecutor.php b/src/Infra/Usecase/LaravelExecutor.php
index 4c06696..78625bf 100644
--- a/src/Infra/Usecase/LaravelExecutor.php
+++ b/src/Infra/Usecase/LaravelExecutor.php
@@ -11,7 +11,7 @@ use Shared\DDD\Usecase\Input;
use Shared\DDD\Usecase\Presenter;
use Shared\DDD\Usecase\Usecase;
-/** @implements UsecaseExecutor */
+/** @implements UsecaseExecutor */
final readonly class LaravelExecutor implements UsecaseExecutor
{
public function __construct(
@@ -21,8 +21,11 @@ final readonly class LaravelExecutor implements UsecaseExecutor
}
/**
- * @param Usecase $usecase
- * @param Presenter<\Shared\DDD\Usecase\Output,object> $presenter
+ * @template TView of object
+ *
+ * @param Usecase $usecase
+ * @param Presenter<\Shared\DDD\Usecase\Output, TView> $presenter
+ * @return TView
*/
#[\Override]
public function execute(Usecase $usecase, Input $input, Presenter $presenter): object
@@ -36,7 +39,7 @@ final readonly class LaravelExecutor implements UsecaseExecutor
$this->logger->debug('Usecase execution started', $context);
try {
- /** @var object $result */
+ /** @var TView $result */
$result = $this->database->transaction(function () use ($usecase, $input, $presenter): object {
$output = $usecase->execute($input);
diff --git a/src/Presentation/Api/V1/Contracts/ApiResponse.php b/src/Presentation/Api/V1/Contracts/ApiResponse.php
index 352e467..62f77c0 100644
--- a/src/Presentation/Api/V1/Contracts/ApiResponse.php
+++ b/src/Presentation/Api/V1/Contracts/ApiResponse.php
@@ -4,9 +4,10 @@ declare(strict_types=1);
namespace Presentation\Api\V1\Contracts;
+use Illuminate\Contracts\Support\Responsable;
use Illuminate\Http\JsonResponse;
-interface ApiResponse
+interface ApiResponse extends Responsable
{
public function present(): JsonResponse;
}
diff --git a/src/Presentation/Api/V1/Http/Controllers/Employees/EmployeeShowController.php b/src/Presentation/Api/V1/Http/Controllers/Employees/EmployeeShowController.php
index 42ae0fa..1a3066e 100644
--- a/src/Presentation/Api/V1/Http/Controllers/Employees/EmployeeShowController.php
+++ b/src/Presentation/Api/V1/Http/Controllers/Employees/EmployeeShowController.php
@@ -6,7 +6,7 @@ namespace Presentation\Api\V1\Http\Controllers\Employees;
use Application\Usecase\Employees\Find\FindEmployee;
use Application\Usecase\Employees\Find\Input;
-use Illuminate\Http\JsonResponse;
+use Presentation\Api\V1\Contracts\ApiResponse;
use Presentation\Api\V1\Http\Requests\Employees\EmployeeShowRequest;
use Presentation\Api\V1\Http\Responses\Employees\EmployeeShowResponse;
use Presentation\Api\V1\Presenters\Employees\EmployeeShowPresenter;
@@ -23,17 +23,15 @@ final readonly class EmployeeShowController
) {
}
- public function __invoke(EmployeeShowRequest $request): JsonResponse
+ public function __invoke(EmployeeShowRequest $request): ApiResponse
{
-
- /** @var \Presentation\Api\V1\Presenters\Employees\EmployeeShowView $response */
$response = UsecaseBuilder::using($this->executor)
->do($this->usecase)
->from($this->toInput($request))
->with($this->presenter)
->run();
- return (new EmployeeShowResponse($response))->present();
+ return new EmployeeShowResponse($response);
}
private function toInput(EmployeeShowRequest $request): Input
diff --git a/src/Presentation/Api/V1/Http/Controllers/Employees/EmployeesIndexController.php b/src/Presentation/Api/V1/Http/Controllers/Employees/EmployeesIndexController.php
index ea25050..d594fdc 100644
--- a/src/Presentation/Api/V1/Http/Controllers/Employees/EmployeesIndexController.php
+++ b/src/Presentation/Api/V1/Http/Controllers/Employees/EmployeesIndexController.php
@@ -6,7 +6,7 @@ namespace Presentation\Api\V1\Http\Controllers\Employees;
use Application\Usecase\Employees\FindAll\FindAllEmployees;
use Application\Usecase\Employees\FindAll\Input;
-use Illuminate\Http\JsonResponse;
+use Presentation\Api\V1\Contracts\ApiResponse;
use Presentation\Api\V1\Http\Requests\Employees\EmployeesIndexRequest;
use Presentation\Api\V1\Http\Responses\Employees\EmployeesIndexResponse;
use Presentation\Api\V1\Presenters\Employees\EmployeesIndexPresenter;
@@ -23,21 +23,25 @@ final readonly class EmployeesIndexController
) {
}
- public function __invoke(EmployeesIndexRequest $request): JsonResponse
+ public function __invoke(EmployeesIndexRequest $request): ApiResponse
{
- $input = new Input(
- pagination: $request->pagination(),
- sorter: $request->sorter(),
- filter: $request->filter(),
- );
+ $input = $this->inputFrom($request);
- /** @var \Presentation\Api\V1\Presenters\Employees\EmployeesIndexView $response */
$response = UsecaseBuilder::using($this->executor)
->do($this->usecase)
->from($input)
->with($this->presenter)
->run();
- return (new EmployeesIndexResponse($response, $request->toLeagueUri()))->present();
+ return new EmployeesIndexResponse($response, $request->toLeagueUri());
+ }
+
+ private function inputFrom(EmployeesIndexRequest $request): Input
+ {
+ return new Input(
+ pagination: $request->pagination(),
+ sorter: $request->sorter(),
+ filter: $request->filter(),
+ );
}
}
diff --git a/src/Presentation/Api/V1/Http/Controllers/IncoSheets/IncoSheetsIndexController.php b/src/Presentation/Api/V1/Http/Controllers/IncoSheets/IncoSheetsIndexController.php
index 82ffac6..25146a6 100644
--- a/src/Presentation/Api/V1/Http/Controllers/IncoSheets/IncoSheetsIndexController.php
+++ b/src/Presentation/Api/V1/Http/Controllers/IncoSheets/IncoSheetsIndexController.php
@@ -6,7 +6,7 @@ namespace Presentation\Api\V1\Http\Controllers\IncoSheets;
use Application\Usecase\IncoSheets\FindAll\FindAllIncoSheets;
use Application\Usecase\IncoSheets\FindAll\Input;
-use Illuminate\Http\JsonResponse;
+use Presentation\Api\V1\Contracts\ApiResponse;
use Presentation\Api\V1\Http\Requests\IncoSheets\IncoSheetsIndexRequest;
use Presentation\Api\V1\Http\Responses\IncoSheets\IncoSheetsIndexResponse;
use Presentation\Api\V1\Presenters\IncoSheets\IncoSheetsIndexPresenter;
@@ -23,7 +23,7 @@ final readonly class IncoSheetsIndexController
) {
}
- public function __invoke(IncoSheetsIndexRequest $request): JsonResponse
+ public function __invoke(IncoSheetsIndexRequest $request): ApiResponse
{
$input = new Input(
pagination: $request->pagination(),
@@ -31,13 +31,12 @@ final readonly class IncoSheetsIndexController
filter: $request->filter(),
);
- /** @var \Presentation\Api\V1\Presenters\IncoSheets\IncoSheetsIndexView $response */
$response = UsecaseBuilder::using($this->executor)
->do($this->usecase)
->from($input)
->with($this->presenter)
->run();
- return (new IncoSheetsIndexResponse($response, $request->toLeagueUri(), $request->isShort()))->present();
+ return new IncoSheetsIndexResponse($response, $request->toLeagueUri());
}
}
diff --git a/src/Presentation/Api/V1/Http/Controllers/IncoSheets/IncoSheetsShowController.php b/src/Presentation/Api/V1/Http/Controllers/IncoSheets/IncoSheetsShowController.php
index 0e08ad5..ca9c3f7 100644
--- a/src/Presentation/Api/V1/Http/Controllers/IncoSheets/IncoSheetsShowController.php
+++ b/src/Presentation/Api/V1/Http/Controllers/IncoSheets/IncoSheetsShowController.php
@@ -6,7 +6,7 @@ namespace Presentation\Api\V1\Http\Controllers\IncoSheets;
use Application\Usecase\IncoSheets\Find\FindIncoSheet;
use Application\Usecase\IncoSheets\Find\Input;
-use Illuminate\Http\JsonResponse;
+use Presentation\Api\V1\Contracts\ApiResponse;
use Presentation\Api\V1\Http\Requests\IncoSheets\IncoSheetsShowRequest;
use Presentation\Api\V1\Http\Responses\IncoSheets\IncoSheetsShowResponse;
use Presentation\Api\V1\Presenters\IncoSheets\IncoSheetsShowPresenter;
@@ -23,19 +23,18 @@ final readonly class IncoSheetsShowController
) {
}
- public function __invoke(IncoSheetsShowRequest $request): JsonResponse
+ public function __invoke(IncoSheetsShowRequest $request): ApiResponse
{
$input = new Input(
id: $request->incoId(),
);
- /** @var \Presentation\Api\V1\Presenters\IncoSheets\IncoSheetsShowView $response */
$response = UsecaseBuilder::using($this->executor)
->do($this->usecase)
->from($input)
->with($this->presenter)
->run();
- return (new IncoSheetsShowResponse($response))->present();
+ return new IncoSheetsShowResponse($response);
}
}
diff --git a/src/Presentation/Api/V1/Http/Middleware/TransformApiV1Response.php b/src/Presentation/Api/V1/Http/Middleware/TransformApiV1Response.php
new file mode 100644
index 0000000..c27275c
--- /dev/null
+++ b/src/Presentation/Api/V1/Http/Middleware/TransformApiV1Response.php
@@ -0,0 +1,25 @@
+present();
+ }
+
+ /** @var Response $response */
+ return $response;
+ }
+}
diff --git a/src/Presentation/Api/V1/Http/Responses/Employees/EmployeeShowResponse.php b/src/Presentation/Api/V1/Http/Responses/Employees/EmployeeShowResponse.php
index e5296ec..4c0cf48 100644
--- a/src/Presentation/Api/V1/Http/Responses/Employees/EmployeeShowResponse.php
+++ b/src/Presentation/Api/V1/Http/Responses/Employees/EmployeeShowResponse.php
@@ -16,6 +16,12 @@ final readonly class EmployeeShowResponse implements ApiResponse
) {
}
+ #[\Override]
+ public function toResponse($request): JsonResponse
+ {
+ return $this->present();
+ }
+
#[\Override]
public function present(): JsonResponse
{
diff --git a/src/Presentation/Api/V1/Http/Responses/Employees/EmployeesIndexResponse.php b/src/Presentation/Api/V1/Http/Responses/Employees/EmployeesIndexResponse.php
index a530e78..1dcae08 100644
--- a/src/Presentation/Api/V1/Http/Responses/Employees/EmployeesIndexResponse.php
+++ b/src/Presentation/Api/V1/Http/Responses/Employees/EmployeesIndexResponse.php
@@ -19,6 +19,12 @@ final readonly class EmployeesIndexResponse implements ApiResponse
) {
}
+ #[\Override]
+ public function toResponse($request): JsonResponse
+ {
+ return $this->present();
+ }
+
#[\Override]
public function present(): JsonResponse
{
diff --git a/src/Presentation/Api/V1/Http/Responses/IncoSheets/IncoSheetsIndexResponse.php b/src/Presentation/Api/V1/Http/Responses/IncoSheets/IncoSheetsIndexResponse.php
index 4b86d70..61c4b6d 100644
--- a/src/Presentation/Api/V1/Http/Responses/IncoSheets/IncoSheetsIndexResponse.php
+++ b/src/Presentation/Api/V1/Http/Responses/IncoSheets/IncoSheetsIndexResponse.php
@@ -18,10 +18,15 @@ final readonly class IncoSheetsIndexResponse implements ApiResponse
public function __construct(
private IncoSheetsIndexView $view,
private Http $baseUri,
- private bool $isShort = false,
) {
}
+ #[\Override]
+ public function toResponse($request): JsonResponse
+ {
+ return $this->present();
+ }
+
#[\Override]
public function present(): JsonResponse
{
@@ -36,31 +41,15 @@ final readonly class IncoSheetsIndexResponse implements ApiResponse
$prevUrl = $page > 1 ? (string) $this->baseUri->withQuery(http_build_query(['page' => $page - 1, 'pageSize' => $pageSize])) : null;
$nextUrl = $page < $totalPages ? (string) $this->baseUri->withQuery(http_build_query(['page' => $page + 1, 'pageSize' => $pageSize])) : null;
- $isShort = $this->isShort;
-
- $incoSheets = array_map(
- static function (PaginatedIncoSheet | PaginatedShortIncoSheet $sheet) use ($isShort): array {
- if ($isShort || $sheet instanceof PaginatedShortIncoSheet) {
- $shortSheet = $sheet instanceof PaginatedShortIncoSheet
- ? $sheet
- : new PaginatedShortIncoSheet(
- id: $sheet->id,
- name: $sheet->name,
- label: $sheet->label,
- createdAt: $sheet->createdAt,
- updatedAt: $sheet->updatedAt,
- );
-
- return get_object_vars(IncoShortSheetDto::fromPaginatedShortIncoSheet($shortSheet));
- }
-
- return get_object_vars(IncoSheetDto::fromPaginatedIncoSheet($sheet));
- },
+ $items = array_map(
+ fn (PaginatedIncoSheet|PaginatedShortIncoSheet $sheet): array => $sheet instanceof PaginatedShortIncoSheet
+ ? get_object_vars(IncoShortSheetDto::fromPaginatedShortIncoSheet($sheet))
+ : get_object_vars(IncoSheetDto::fromPaginatedIncoSheet($sheet)),
$this->view->incoSheets
);
return response()->json([
- 'incoSheets' => $incoSheets,
+ 'incoSheets' => $items,
'pagination' => [
'page' => $page,
'pageSize' => $pageSize,
diff --git a/src/Presentation/Api/V1/Http/Responses/IncoSheets/IncoSheetsShowResponse.php b/src/Presentation/Api/V1/Http/Responses/IncoSheets/IncoSheetsShowResponse.php
index 9615324..113a560 100644
--- a/src/Presentation/Api/V1/Http/Responses/IncoSheets/IncoSheetsShowResponse.php
+++ b/src/Presentation/Api/V1/Http/Responses/IncoSheets/IncoSheetsShowResponse.php
@@ -16,6 +16,12 @@ final readonly class IncoSheetsShowResponse implements ApiResponse
) {
}
+ #[\Override]
+ public function toResponse($request): JsonResponse
+ {
+ return $this->present();
+ }
+
#[\Override]
public function present(): JsonResponse
{
diff --git a/src/Shared/DDD/Usecase/Builder/BuilderStepBuild.php b/src/Shared/DDD/Usecase/Builder/BuilderStepBuild.php
index 4cee856..70a60d4 100644
--- a/src/Shared/DDD/Usecase/Builder/BuilderStepBuild.php
+++ b/src/Shared/DDD/Usecase/Builder/BuilderStepBuild.php
@@ -4,7 +4,9 @@ declare(strict_types=1);
namespace Shared\DDD\Usecase\Builder;
+/** @template-covariant TView of object */
interface BuilderStepBuild
{
+ /** @return TView */
public function run(): object;
}
diff --git a/src/Shared/DDD/Usecase/Builder/BuilderStepPresenter.php b/src/Shared/DDD/Usecase/Builder/BuilderStepPresenter.php
index ef8728b..5334c02 100644
--- a/src/Shared/DDD/Usecase/Builder/BuilderStepPresenter.php
+++ b/src/Shared/DDD/Usecase/Builder/BuilderStepPresenter.php
@@ -9,6 +9,11 @@ use Shared\DDD\Usecase\Presenter;
/** @template TOutput of \Shared\DDD\Usecase\Output */
interface BuilderStepPresenter
{
- /** @param Presenter $presenter */
+ /**
+ * @template TView of object
+ *
+ * @param Presenter $presenter
+ * @return BuilderStepBuild
+ */
public function with(Presenter $presenter): BuilderStepBuild;
}
diff --git a/src/Shared/DDD/Usecase/Builder/BuilderStepUsecase.php b/src/Shared/DDD/Usecase/Builder/BuilderStepUsecase.php
index ad0cc80..3aa7a60 100644
--- a/src/Shared/DDD/Usecase/Builder/BuilderStepUsecase.php
+++ b/src/Shared/DDD/Usecase/Builder/BuilderStepUsecase.php
@@ -6,11 +6,14 @@ namespace Shared\DDD\Usecase\Builder;
use Shared\DDD\Usecase\Usecase;
-/** @template TOutput of \Shared\DDD\Usecase\Output */
+/**
+ * @template TInput of \Shared\DDD\Usecase\Input
+ * @template TOutput of \Shared\DDD\Usecase\Output
+ */
interface BuilderStepUsecase
{
/**
- * @param Usecase<\Shared\DDD\Usecase\Input,TOutput> $usecase
+ * @param Usecase $usecase
* @return BuilderStepInput
*/
public function do(Usecase $usecase): BuilderStepInput;
diff --git a/src/Shared/DDD/Usecase/Builder/UsecaseBuilder.php b/src/Shared/DDD/Usecase/Builder/UsecaseBuilder.php
index 835a013..e2d28ef 100644
--- a/src/Shared/DDD/Usecase/Builder/UsecaseBuilder.php
+++ b/src/Shared/DDD/Usecase/Builder/UsecaseBuilder.php
@@ -10,8 +10,8 @@ final class UsecaseBuilder
* @template TInput of \Shared\DDD\Usecase\Input
* @template TOutput of \Shared\DDD\Usecase\Output
*
- * @param UsecaseExecutor $executor
- * @return BuilderStepUsecase
+ * @param UsecaseExecutor $executor
+ * @return BuilderStepUsecase
*/
public static function using(UsecaseExecutor $executor): BuilderStepUsecase
{
diff --git a/src/Shared/DDD/Usecase/Builder/UsecaseExecutionBuilder.php b/src/Shared/DDD/Usecase/Builder/UsecaseExecutionBuilder.php
index fb6ca89..6a3bfbf 100644
--- a/src/Shared/DDD/Usecase/Builder/UsecaseExecutionBuilder.php
+++ b/src/Shared/DDD/Usecase/Builder/UsecaseExecutionBuilder.php
@@ -12,28 +12,30 @@ use Shared\DDD\Usecase\Usecase;
/**
* @template TInput of \Shared\DDD\Usecase\Input
* @template TOutput of \Shared\DDD\Usecase\Output
+ * @template TView of object
*
* @implements BuilderStepInput
* @implements BuilderStepPresenter
- * @implements BuilderStepUsecase
+ * @implements BuilderStepUsecase
+ * @implements BuilderStepBuild
*/
final class UsecaseExecutionBuilder implements BuilderStepBuild, BuilderStepInput, BuilderStepPresenter, BuilderStepUsecase
{
- /** @param UsecaseExecutor $executor */
+ /** @param UsecaseExecutor $executor */
public function __construct(
private readonly UsecaseExecutor $executor,
) {
}
- /** @var Usecase|null */
+ /** @var Usecase|null */
private ?Usecase $usecase = null;
private ?Input $input = null;
- /** @var Presenter|null */
+ /** @var Presenter|null */
private ?Presenter $presenter = null;
- /** @param Usecase $usecase */
+ /** @param Usecase $usecase */
#[\Override]
public function do(Usecase $usecase): BuilderStepInput
{
@@ -54,7 +56,12 @@ final class UsecaseExecutionBuilder implements BuilderStepBuild, BuilderStepInpu
return $this;
}
- /** @param Presenter $presenter */
+ /**
+ * @template TNextView of object
+ *
+ * @param Presenter $presenter
+ * @return BuilderStepBuild
+ */
#[\Override]
public function with(Presenter $presenter): BuilderStepBuild
{
@@ -69,6 +76,7 @@ final class UsecaseExecutionBuilder implements BuilderStepBuild, BuilderStepInpu
return $this;
}
+ /** @return TView */
#[\Override]
public function run(): object
{
@@ -76,6 +84,7 @@ final class UsecaseExecutionBuilder implements BuilderStepBuild, BuilderStepInpu
throw new LogicException('Usecase, input and presenter are all required.');
}
+ /** @var TView */
return $this->executor->execute(
usecase: $this->usecase,
input: $this->input,
diff --git a/src/Shared/DDD/Usecase/Builder/UsecaseExecutor.php b/src/Shared/DDD/Usecase/Builder/UsecaseExecutor.php
index 7d797b1..2e5b246 100644
--- a/src/Shared/DDD/Usecase/Builder/UsecaseExecutor.php
+++ b/src/Shared/DDD/Usecase/Builder/UsecaseExecutor.php
@@ -15,8 +15,11 @@ use Shared\DDD\Usecase\Usecase;
interface UsecaseExecutor
{
/**
- * @param Usecase $usecase
- * @param Presenter $presenter
+ * @template TView of object
+ *
+ * @param Usecase $usecase
+ * @param Presenter $presenter
+ * @return TView
*/
public function execute(Usecase $usecase, Input $input, Presenter $presenter): object;
}