diff --git a/src/Application/Usecase/IncoSheets/FindAll/FindAllIncoSheets.php b/src/Application/Usecase/IncoSheets/FindAll/FindAllIncoSheets.php index ba92660..9d4a02a 100644 --- a/src/Application/Usecase/IncoSheets/FindAll/FindAllIncoSheets.php +++ b/src/Application/Usecase/IncoSheets/FindAll/FindAllIncoSheets.php @@ -5,10 +5,12 @@ declare(strict_types=1); namespace Application\Usecase\IncoSheets\FindAll; use Domain\Models\Entities\Inco\IncoSheet; +use Domain\Models\Entities\Inco\IncoShortSheet; use Domain\Models\ValueObjects\Inco\IncoSheetFilter as DirectoryIncoSheetFilter; use Domain\Models\ValueObjects\Inco\IncoSheetSortColumn as DirectoryIncoSheetSortColumn; use Domain\Models\ValueObjects\Inco\IncoSheetSorter as DirectoryIncoSheetSorter; use Domain\Ports\Directories\IncoSheetDirectory; +use Domain\Ports\Directories\IncoShortSheetDirectory; use Shared\DDD\Usecase\Input as BaseInput; use Shared\DDD\Usecase\Usecase; use Shared\Paginated; @@ -18,7 +20,8 @@ use Webmozart\Assert\Assert; final readonly class FindAllIncoSheets implements Usecase { public function __construct( - private IncoSheetDirectory $directory + private IncoSheetDirectory $directory, + private IncoShortSheetDirectory $shortDirectory, ) { } @@ -36,26 +39,54 @@ final readonly class FindAllIncoSheets implements Usecase direction: $input->sorter->direction, ); + if ($input->short) { + $shortPage = $this->shortDirectory->findAll( + pagination: $input->pagination, + sorter: $sorter, + filter: $filter + ); + + return $this->toShortOutput($shortPage); + } + $page = $this->directory->findAll( pagination: $input->pagination, sorter: $sorter, filter: $filter ); - return $this->toOutput($page, $input->short); + return $this->toFullOutput($page); } /** * @param Paginated $resultPage */ - private function toOutput(Paginated $resultPage, bool $short): Output + private function toFullOutput(Paginated $resultPage): Output { $pagination = $resultPage->getPagination(); $items = array_map( - static fn (IncoSheet $sheet): PaginatedIncoSheet | PaginatedShortIncoSheet => $short - ? PaginatedShortIncoSheet::fromDomain($sheet) - : PaginatedIncoSheet::fromDomain($sheet), + static fn (IncoSheet $sheet) => PaginatedIncoSheet::fromDomain($sheet), + $resultPage->getItems() + ); + + return new Output( + incoSheets: $items, + total: $resultPage->getTotal(), + page: $pagination->pageNumber(), + perPage: $pagination->pageSize(), + ); + } + + /** + * @param Paginated $resultPage + */ + private function toShortOutput(Paginated $resultPage): Output + { + $pagination = $resultPage->getPagination(); + + $items = array_map( + static fn (IncoShortSheet $sheet) => PaginatedShortIncoSheet::fromDomain($sheet), $resultPage->getItems() ); diff --git a/src/Application/Usecase/IncoSheets/FindAll/PaginatedShortIncoSheet.php b/src/Application/Usecase/IncoSheets/FindAll/PaginatedShortIncoSheet.php index fc94a21..c9121b9 100644 --- a/src/Application/Usecase/IncoSheets/FindAll/PaginatedShortIncoSheet.php +++ b/src/Application/Usecase/IncoSheets/FindAll/PaginatedShortIncoSheet.php @@ -4,7 +4,7 @@ declare(strict_types=1); namespace Application\Usecase\IncoSheets\FindAll; -use Domain\Models\Entities\Inco\IncoSheet; +use Domain\Models\Entities\Inco\IncoShortSheet; use Domain\Models\ValueObjects\Inco\IncoId; use Shared\DDD\Usecase\Output as UsecaseOutput; @@ -19,7 +19,7 @@ final readonly class PaginatedShortIncoSheet implements UsecaseOutput ) { } - public static function fromDomain(IncoSheet $incoSheet): self + public static function fromDomain(IncoShortSheet $incoSheet): self { return new self( id: $incoSheet->id, diff --git a/src/Application/Usecase/IncoSheets/Search/Input.php b/src/Application/Usecase/IncoSheets/Search/Input.php index 6165e57..b62e843 100644 --- a/src/Application/Usecase/IncoSheets/Search/Input.php +++ b/src/Application/Usecase/IncoSheets/Search/Input.php @@ -6,6 +6,7 @@ namespace Application\Usecase\IncoSheets\Search; use Domain\Models\ValueObjects\Inco\IncoIdQuery; use Shared\DDD\Usecase\Input as UsecaseInput; +use Shared\Pagination; final readonly class Input implements UsecaseInput { @@ -15,6 +16,7 @@ final readonly class Input implements UsecaseInput public function __construct( public IncoIdQuery $query, public bool $short = false, + public ?Pagination $pagination = null, string ...$brands, ) { $this->brands = $brands; diff --git a/src/Application/Usecase/IncoSheets/Search/PaginatedShortIncoSheet.php b/src/Application/Usecase/IncoSheets/Search/PaginatedShortIncoSheet.php index 8875ecc..9aa9316 100644 --- a/src/Application/Usecase/IncoSheets/Search/PaginatedShortIncoSheet.php +++ b/src/Application/Usecase/IncoSheets/Search/PaginatedShortIncoSheet.php @@ -4,7 +4,7 @@ declare(strict_types=1); namespace Application\Usecase\IncoSheets\Search; -use Domain\Models\Entities\Inco\IncoSheet; +use Domain\Models\Entities\Inco\IncoShortSheet; use Domain\Models\ValueObjects\Inco\IncoId; use Shared\DDD\Usecase\Output as UsecaseOutput; @@ -19,7 +19,7 @@ final readonly class PaginatedShortIncoSheet implements UsecaseOutput ) { } - public static function fromDomain(IncoSheet $incoSheet): self + public static function fromDomain(IncoShortSheet $incoSheet): self { return new self( id: $incoSheet->id, diff --git a/src/Application/Usecase/IncoSheets/Search/SearchIncoSheets.php b/src/Application/Usecase/IncoSheets/Search/SearchIncoSheets.php index 03032b1..e35579c 100644 --- a/src/Application/Usecase/IncoSheets/Search/SearchIncoSheets.php +++ b/src/Application/Usecase/IncoSheets/Search/SearchIncoSheets.php @@ -5,17 +5,21 @@ declare(strict_types=1); namespace Application\Usecase\IncoSheets\Search; use Domain\Models\Entities\Inco\IncoSheet; +use Domain\Models\Entities\Inco\IncoShortSheet; use Domain\Ports\Directories\IncoSheetDirectory; +use Domain\Ports\Directories\IncoShortSheetDirectory; use Shared\DDD\Usecase\Input as BaseInput; use Shared\DDD\Usecase\Usecase; use Shared\Paginated; +use Shared\Pagination; use Webmozart\Assert\Assert; /** @implements Usecase<\Shared\DDD\Usecase\Input, \Shared\DDD\Usecase\Output> */ final readonly class SearchIncoSheets implements Usecase { public function __construct( - private IncoSheetDirectory $directory + private IncoSheetDirectory $directory, + private IncoShortSheetDirectory $shortDirectory, ) { } @@ -24,25 +28,56 @@ final readonly class SearchIncoSheets implements Usecase { Assert::isInstanceOf($input, Input::class); + $pagination = $input->pagination ?? Pagination::firstPage(); + + if ($input->short) { + $shortPage = $this->shortDirectory->search( + $input->query, + $pagination, + ...$input->brands + ); + + return $this->toShortOutput($shortPage); + } + $page = $this->directory->search( $input->query, + $pagination, ...$input->brands ); - return $this->toOutput($page, $input->short); + return $this->toFullOutput($page); } /** * @param Paginated $resultPage */ - private function toOutput(Paginated $resultPage, bool $short): Output + private function toFullOutput(Paginated $resultPage): Output { $pagination = $resultPage->getPagination(); $items = array_map( - static fn (IncoSheet $sheet): PaginatedIncoSheet | PaginatedShortIncoSheet => $short - ? PaginatedShortIncoSheet::fromDomain($sheet) - : PaginatedIncoSheet::fromDomain($sheet), + static fn (IncoSheet $sheet) => PaginatedIncoSheet::fromDomain($sheet), + $resultPage->getItems() + ); + + return new Output( + incoSheets: $items, + total: $resultPage->getTotal(), + page: $pagination->pageNumber(), + perPage: $pagination->pageSize(), + ); + } + + /** + * @param Paginated $resultPage + */ + private function toShortOutput(Paginated $resultPage): Output + { + $pagination = $resultPage->getPagination(); + + $items = array_map( + static fn (IncoShortSheet $sheet) => PaginatedShortIncoSheet::fromDomain($sheet), $resultPage->getItems() ); diff --git a/src/Domain/Models/Entities/Inco/IncoShortSheet.php b/src/Domain/Models/Entities/Inco/IncoShortSheet.php new file mode 100644 index 0000000..670617a --- /dev/null +++ b/src/Domain/Models/Entities/Inco/IncoShortSheet.php @@ -0,0 +1,27 @@ + */ +final readonly class IncoShortSheet implements Entity +{ + public function __construct( + public IncoId $id, + public string $name, + public string $label, + public ?\DateTimeInterface $createdAt, + public ?\DateTimeInterface $updatedAt, + ) { + } + + #[\Override] + public function id(): IncoId + { + return $this->id; + } +} diff --git a/src/Domain/Ports/Directories/IncoSheetDirectory.php b/src/Domain/Ports/Directories/IncoSheetDirectory.php index b5f4f34..55f6ddc 100644 --- a/src/Domain/Ports/Directories/IncoSheetDirectory.php +++ b/src/Domain/Ports/Directories/IncoSheetDirectory.php @@ -26,6 +26,7 @@ interface IncoSheetDirectory extends Directory /** @return Paginated */ public function search( IncoIdQuery $query, + Pagination $pagination, string ...$brands ): Paginated; diff --git a/src/Domain/Ports/Directories/IncoShortSheetDirectory.php b/src/Domain/Ports/Directories/IncoShortSheetDirectory.php new file mode 100644 index 0000000..11543d7 --- /dev/null +++ b/src/Domain/Ports/Directories/IncoShortSheetDirectory.php @@ -0,0 +1,34 @@ + */ +interface IncoShortSheetDirectory extends Directory +{ + /** @return Paginated */ + public function findAll( + Pagination $pagination, + IncoSheetSorter $sorter, + ?IncoSheetFilter $filter = null + ): Paginated; + + /** @return Paginated */ + public function search( + IncoIdQuery $query, + Pagination $pagination, + string ...$brands + ): Paginated; + + public function find(IncoId $id): ?IncoShortSheet; +} diff --git a/src/Infra/Adapters/Directories/EloquentIncoSheetDirectory.php b/src/Infra/Adapters/Directories/EloquentIncoSheetDirectory.php index ab9f0ff..44ebd33 100644 --- a/src/Infra/Adapters/Directories/EloquentIncoSheetDirectory.php +++ b/src/Infra/Adapters/Directories/EloquentIncoSheetDirectory.php @@ -72,6 +72,7 @@ final class EloquentIncoSheetDirectory implements IncoSheetDirectory #[\Override] public function search( IncoIdQuery $query, + Pagination $pagination, string ...$brands ): Paginated { $dbQuery = IncoSheetModel::query(); @@ -84,7 +85,10 @@ final class EloquentIncoSheetDirectory implements IncoSheetDirectory $total = (int) (clone $dbQuery)->count(); - $rows = $dbQuery->get(); + $rows = $dbQuery + ->offset($pagination->from()) + ->limit($pagination->pageSize()) + ->get(); $items = []; @@ -92,8 +96,6 @@ final class EloquentIncoSheetDirectory implements IncoSheetDirectory $items[] = $this->toDomain($row); } - $pagination = Pagination::ofPage(1, max(1, $total)); - /** @var IncoSheet[] $items */ return new Paginated($pagination, $items, $total); } diff --git a/src/Infra/Adapters/Directories/EloquentIncoShortSheetDirectory.php b/src/Infra/Adapters/Directories/EloquentIncoShortSheetDirectory.php new file mode 100644 index 0000000..a3e9722 --- /dev/null +++ b/src/Infra/Adapters/Directories/EloquentIncoShortSheetDirectory.php @@ -0,0 +1,140 @@ +select(self::SHORT_FIELDS); + + if ($filter !== null) { + if ($filter->since !== null) { + $query->where(static function ($q) use ($filter): void { + $q->where('updated_at', '>=', $filter->since) + ->orWhere('created_at', '>=', $filter->since); + }); + } + + if (! empty($filter->brands)) { + $query->whereIn('brand->code', $filter->brands); + } + } + + $total = (int) (clone $query)->count(); + + $rows = (clone $query) + ->orderBy( + $this->toSqlColumn($sorter->sortBy), + $sorter->direction + ) + ->offset($pagination->from()) + ->limit($pagination->pageSize()) + ->get(); + + $items = []; + foreach ($rows as $row) { + $items[] = $this->toDomain($row); + } + + /** @var IncoShortSheet[] $items */ + return new Paginated($pagination, $items, $total); + } + + #[\Override] + public function search( + IncoIdQuery $query, + Pagination $pagination, + string ...$brands + ): Paginated { + $dbQuery = IncoSheetModel::query()->select(self::SHORT_FIELDS); + + $dbQuery->where('id', 'LIKE', $query->value); + + if (! empty($brands)) { + $dbQuery->whereIn('brand->code', $brands); + } + + $total = (int) (clone $dbQuery)->count(); + + $rows = $dbQuery + ->offset($pagination->from()) + ->limit($pagination->pageSize()) + ->get(); + + $items = []; + foreach ($rows as $row) { + $items[] = $this->toDomain($row); + } + + /** @var IncoShortSheet[] $items */ + return new Paginated($pagination, $items, $total); + } + + #[\Override] + public function find(IncoId $id): ?IncoShortSheet + { + $model = IncoSheetModel::query() + ->select(self::SHORT_FIELDS) + ->where('id', $id->value) + ->first(); + + if ($model === null) { + return null; + } + + return $this->toDomain($model); + } + + private function toDomain(IncoSheetModel $model): IncoShortSheet + { + $rawId = $model->getAttribute('id'); + $rawName = $model->getAttribute('name'); + $rawLabel = $model->getAttribute('label'); + $rawCreatedAt = $model->getAttribute('created_at'); + $rawUpdatedAt = $model->getAttribute('updated_at'); + + $id = is_string($rawId) || is_numeric($rawId) ? (string) $rawId : ''; + $name = is_string($rawName) ? $rawName : ''; + $label = is_string($rawLabel) ? $rawLabel : ''; + $createdAt = is_string($rawCreatedAt) ? DatesHelper::parseDateTime($rawCreatedAt) : null; + $updatedAt = is_string($rawUpdatedAt) ? DatesHelper::parseDateTime($rawUpdatedAt) : null; + + return new IncoShortSheet( + id: new IncoId($id), + name: $name, + label: $label, + createdAt: $createdAt, + updatedAt: $updatedAt, + ); + } + + private function toSqlColumn(IncoSheetSortColumn $column): string + { + return match ($column) { + IncoSheetSortColumn::incoSheetId => 'id', + IncoSheetSortColumn::createdAt => 'created_at', + IncoSheetSortColumn::updatedAt => 'updated_at', + }; + } +} diff --git a/src/Infra/Providers/DomainServiceProvider.php b/src/Infra/Providers/DomainServiceProvider.php index 3539920..8821b4b 100644 --- a/src/Infra/Providers/DomainServiceProvider.php +++ b/src/Infra/Providers/DomainServiceProvider.php @@ -7,10 +7,12 @@ namespace Infra\Providers; use Domain\Ports\Directories\EmployeeDirectory; use Domain\Ports\Directories\IncoCompositionDirectory; use Domain\Ports\Directories\IncoSheetDirectory; +use Domain\Ports\Directories\IncoShortSheetDirectory; use Illuminate\Support\ServiceProvider; use Infra\Adapters\Directories\EloquentEmployeeDirectory; use Infra\Adapters\Directories\EloquentIncoCompositionDirectory; use Infra\Adapters\Directories\EloquentIncoSheetDirectory; +use Infra\Adapters\Directories\EloquentIncoShortSheetDirectory; use Infra\Usecase\LaravelExecutor; use Shared\DDD\Usecase\Builder\UsecaseExecutor; @@ -21,6 +23,7 @@ class DomainServiceProvider extends ServiceProvider { $this->app->bind(EmployeeDirectory::class, EloquentEmployeeDirectory::class); $this->app->bind(IncoSheetDirectory::class, EloquentIncoSheetDirectory::class); + $this->app->bind(IncoShortSheetDirectory::class, EloquentIncoShortSheetDirectory::class); $this->app->bind(IncoCompositionDirectory::class, EloquentIncoCompositionDirectory::class); $this->app->singleton(UsecaseExecutor::class, LaravelExecutor::class); } diff --git a/src/Presentation/Api/V1/Http/Controllers/IncoSheets/IncoSheetsSearchController.php b/src/Presentation/Api/V1/Http/Controllers/IncoSheets/IncoSheetsSearchController.php index 4cc0742..7a7898d 100644 --- a/src/Presentation/Api/V1/Http/Controllers/IncoSheets/IncoSheetsSearchController.php +++ b/src/Presentation/Api/V1/Http/Controllers/IncoSheets/IncoSheetsSearchController.php @@ -43,6 +43,7 @@ final readonly class IncoSheetsSearchController return new SearchInput( $request->incoIdSearch(), $request->isShort(), + $request->pagination(), ...$request->brands() ); } diff --git a/tests/Unit/Application/Usecase/Incosheets/FindAllIncoSheetsTest.php b/tests/Unit/Application/Usecase/Incosheets/FindAllIncoSheetsTest.php index 359eabc..6cc91f5 100644 --- a/tests/Unit/Application/Usecase/Incosheets/FindAllIncoSheetsTest.php +++ b/tests/Unit/Application/Usecase/Incosheets/FindAllIncoSheetsTest.php @@ -17,6 +17,7 @@ use Domain\Models\ValueObjects\Inco\IncoSheetSortColumn as DirectoryIncoSheetSor use Domain\Models\ValueObjects\Inco\IncoSheetSorter as DirectoryIncoSheetSorter; use Domain\Models\ValueObjects\Inco\Ingredients; use Domain\Ports\Directories\IncoSheetDirectory; +use Domain\Ports\Directories\IncoShortSheetDirectory; use Shared\Paginated; use Shared\Pagination; @@ -25,11 +26,12 @@ covers(FindAllIncoSheets::class); describe('FindAllIncoSheets Usecase Unit Tests', function (): void { it('executes successfully and returns paginated output DTO', function (): void { // GIVEN - $pagination = Pagination::ofPage(1, 10); - $sorter = new Sorter(SortColumn::incoSheetId, \SortDirection::Ascending); - $input = new Input($pagination, $sorter); - $incoDirectory = \Mockery::mock(IncoSheetDirectory::class); - $usecase = new FindAllIncoSheets($incoDirectory); + $pagination = Pagination::ofPage(1, 10); + $sorter = new Sorter(SortColumn::incoSheetId, \SortDirection::Ascending); + $input = new Input($pagination, $sorter); + $incoDirectory = \Mockery::mock(IncoSheetDirectory::class); + $shortIncoDirectory = \Mockery::mock(IncoShortSheetDirectory::class); + $usecase = new FindAllIncoSheets($incoDirectory, $shortIncoDirectory); $incoId = new IncoId('12345'); $incoSheet = new IncoSheet(