feat(category): include products from all descendant categories in paginated response
This commit is contained in:
@@ -2,8 +2,6 @@
|
||||
|
||||
namespace App\Domains\Commerce\Catalog\Services;
|
||||
|
||||
use App\Shared\Attachable\Models\Attachment;
|
||||
use App\Shared\Attachable\Services\AttachmentService;
|
||||
use App\Domains\Commerce\Catalog\Enums\CatalogItemType;
|
||||
use App\Domains\Commerce\Catalog\Models\Attribute;
|
||||
use App\Domains\Commerce\Catalog\Models\CatalogItem;
|
||||
@@ -11,8 +9,10 @@ use App\Domains\Commerce\Catalog\Models\Category;
|
||||
use App\Domains\Commerce\Catalog\Models\Inventory;
|
||||
use App\Domains\Commerce\Catalog\Models\ItemAttribute;
|
||||
use App\Domains\Commerce\Catalog\Models\Variant;
|
||||
use App\Domains\Ticketing\Event\Models\EventDate;
|
||||
use App\Domains\Core\Tenant\Models\Tenant;
|
||||
use App\Domains\Ticketing\Event\Models\EventDate;
|
||||
use App\Shared\Attachable\Models\Attachment;
|
||||
use App\Shared\Attachable\Services\AttachmentService;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
@@ -286,9 +286,11 @@ class CatalogService
|
||||
int $perPage,
|
||||
int $page,
|
||||
): LengthAwarePaginator {
|
||||
$categoryIds = $this->categoryAndDescendantIds($tenant, $category);
|
||||
|
||||
return CatalogItem::query()
|
||||
->forTenantCatalog($tenant)
|
||||
->where('category_id', $category->id)
|
||||
->whereIn('category_id', $categoryIds)
|
||||
->whereAvailable()
|
||||
->with([
|
||||
'attachments',
|
||||
@@ -306,6 +308,34 @@ class CatalogService
|
||||
->paginate(perPage: $perPage, pageName: 'page', page: $page);
|
||||
}
|
||||
|
||||
/** @return array<int, int> */
|
||||
private function categoryAndDescendantIds(Tenant $tenant, Category $category): array
|
||||
{
|
||||
$childrenByParent = Category::query()
|
||||
->where('tenant_code', $tenant->codigo)
|
||||
->get(['id', 'categoria_id'])
|
||||
->groupBy('categoria_id');
|
||||
|
||||
$categoryIds = [];
|
||||
$pendingIds = [$category->id];
|
||||
|
||||
while ($pendingIds !== []) {
|
||||
$categoryId = array_shift($pendingIds);
|
||||
|
||||
if (isset($categoryIds[$categoryId])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$categoryIds[$categoryId] = $categoryId;
|
||||
|
||||
foreach ($childrenByParent->get($categoryId, collect()) as $child) {
|
||||
$pendingIds[] = $child->id;
|
||||
}
|
||||
}
|
||||
|
||||
return array_values($categoryIds);
|
||||
}
|
||||
|
||||
public function delete(CatalogItem $catalogItem): void
|
||||
{
|
||||
DB::transaction(function () use ($catalogItem): void {
|
||||
|
||||
@@ -72,6 +72,29 @@ class CategoryDetailTest extends TestCase
|
||||
->assertJsonCount(0, 'data');
|
||||
}
|
||||
|
||||
public function test_it_includes_products_from_all_descendant_categories(): void
|
||||
{
|
||||
$tenant = $this->createTenant('category-descendants');
|
||||
$parent = $this->createCategory($tenant, 'Indumentaria');
|
||||
$child = $this->createCategory($tenant, 'Remeras', $parent);
|
||||
$grandchild = $this->createCategory($tenant, 'Manga corta', $child);
|
||||
$sibling = $this->createCategory($tenant, 'Pantalones');
|
||||
|
||||
$parentItem = $this->createCatalogItem($tenant, $parent, 'Producto padre');
|
||||
$childItem = $this->createCatalogItem($tenant, $child, 'Producto hijo');
|
||||
$grandchildItem = $this->createCatalogItem($tenant, $grandchild, 'Producto nieto');
|
||||
$this->createCatalogItem($tenant, $sibling, 'Producto ajeno');
|
||||
|
||||
$this->getJson("/api/tenants/{$tenant->codigo}/categories/{$parent->id}")
|
||||
->assertOk()
|
||||
->assertJsonPath('meta.total', 3)
|
||||
->assertJsonCount(3, 'data')
|
||||
->assertJsonPath('data.0.id', $childItem->id)
|
||||
->assertJsonPath('data.1.id', $grandchildItem->id)
|
||||
->assertJsonPath('data.2.id', $parentItem->id)
|
||||
->assertJsonMissing(['nombre' => 'Producto ajeno']);
|
||||
}
|
||||
|
||||
public function test_it_rejects_categories_from_another_tenant(): void
|
||||
{
|
||||
$tenant = $this->createTenant('category-owner');
|
||||
@@ -107,10 +130,14 @@ class CategoryDetailTest extends TestCase
|
||||
]);
|
||||
}
|
||||
|
||||
private function createCategory(Tenant $tenant, string $name): Category
|
||||
{
|
||||
private function createCategory(
|
||||
Tenant $tenant,
|
||||
string $name,
|
||||
?Category $parent = null,
|
||||
): Category {
|
||||
return Category::query()->create([
|
||||
'tenant_code' => $tenant->codigo,
|
||||
'categoria_id' => $parent?->id,
|
||||
'nombre' => $name,
|
||||
]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user