feat(IncoSheets): introduce IncoShortSheet entity and IncoShortSheetDirectory with SQL select optimization and search pagination
This commit is contained in:
parent
cce81c1a85
commit
bfe0b0010c
13 changed files with 302 additions and 24 deletions
|
|
@ -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<IncoSheet> $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<IncoShortSheet> $resultPage
|
||||
*/
|
||||
private function toShortOutput(Paginated $resultPage): Output
|
||||
{
|
||||
$pagination = $resultPage->getPagination();
|
||||
|
||||
$items = array_map(
|
||||
static fn (IncoShortSheet $sheet) => PaginatedShortIncoSheet::fromDomain($sheet),
|
||||
$resultPage->getItems()
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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<IncoSheet> $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<IncoShortSheet> $resultPage
|
||||
*/
|
||||
private function toShortOutput(Paginated $resultPage): Output
|
||||
{
|
||||
$pagination = $resultPage->getPagination();
|
||||
|
||||
$items = array_map(
|
||||
static fn (IncoShortSheet $sheet) => PaginatedShortIncoSheet::fromDomain($sheet),
|
||||
$resultPage->getItems()
|
||||
);
|
||||
|
||||
|
|
|
|||
27
src/Domain/Models/Entities/Inco/IncoShortSheet.php
Normal file
27
src/Domain/Models/Entities/Inco/IncoShortSheet.php
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Domain\Models\Entities\Inco;
|
||||
|
||||
use Domain\Models\ValueObjects\Inco\IncoId;
|
||||
use Shared\DDD\Entity;
|
||||
|
||||
/** @implements Entity<IncoId> */
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -26,6 +26,7 @@ interface IncoSheetDirectory extends Directory
|
|||
/** @return Paginated<IncoSheet> */
|
||||
public function search(
|
||||
IncoIdQuery $query,
|
||||
Pagination $pagination,
|
||||
string ...$brands
|
||||
): Paginated;
|
||||
|
||||
|
|
|
|||
34
src/Domain/Ports/Directories/IncoShortSheetDirectory.php
Normal file
34
src/Domain/Ports/Directories/IncoShortSheetDirectory.php
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Domain\Ports\Directories;
|
||||
|
||||
use Domain\Models\Entities\Inco\IncoShortSheet;
|
||||
use Domain\Models\ValueObjects\Inco\IncoId;
|
||||
use Domain\Models\ValueObjects\Inco\IncoIdQuery;
|
||||
use Domain\Models\ValueObjects\Inco\IncoSheetFilter;
|
||||
use Domain\Models\ValueObjects\Inco\IncoSheetSorter;
|
||||
use Shared\DDD\Directory;
|
||||
use Shared\Paginated;
|
||||
use Shared\Pagination;
|
||||
|
||||
/** @extends Directory<IncoId, IncoShortSheet> */
|
||||
interface IncoShortSheetDirectory extends Directory
|
||||
{
|
||||
/** @return Paginated<IncoShortSheet> */
|
||||
public function findAll(
|
||||
Pagination $pagination,
|
||||
IncoSheetSorter $sorter,
|
||||
?IncoSheetFilter $filter = null
|
||||
): Paginated;
|
||||
|
||||
/** @return Paginated<IncoShortSheet> */
|
||||
public function search(
|
||||
IncoIdQuery $query,
|
||||
Pagination $pagination,
|
||||
string ...$brands
|
||||
): Paginated;
|
||||
|
||||
public function find(IncoId $id): ?IncoShortSheet;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,140 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Infra\Adapters\Directories;
|
||||
|
||||
use Domain\Models\Entities\Inco\IncoShortSheet;
|
||||
use Domain\Models\ValueObjects\Inco\IncoId;
|
||||
use Domain\Models\ValueObjects\Inco\IncoIdQuery;
|
||||
use Domain\Models\ValueObjects\Inco\IncoSheetFilter;
|
||||
use Domain\Models\ValueObjects\Inco\IncoSheetSortColumn;
|
||||
use Domain\Models\ValueObjects\Inco\IncoSheetSorter;
|
||||
use Domain\Ports\Directories\IncoShortSheetDirectory;
|
||||
use Infra\Database\Models\IncoSheet as IncoSheetModel;
|
||||
use Shared\Helpers\DatesHelper;
|
||||
use Shared\Paginated;
|
||||
use Shared\Pagination;
|
||||
|
||||
final class EloquentIncoShortSheetDirectory implements IncoShortSheetDirectory
|
||||
{
|
||||
private const SHORT_FIELDS = ['id', 'name', 'label', 'created_at', 'updated_at'];
|
||||
|
||||
#[\Override]
|
||||
public function findAll(
|
||||
Pagination $pagination,
|
||||
IncoSheetSorter $sorter,
|
||||
?IncoSheetFilter $filter = null
|
||||
): Paginated {
|
||||
$query = IncoSheetModel::query()->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',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ final readonly class IncoSheetsSearchController
|
|||
return new SearchInput(
|
||||
$request->incoIdSearch(),
|
||||
$request->isShort(),
|
||||
$request->pagination(),
|
||||
...$request->brands()
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue