feat(categories): add category retrieval and pagination for tenant-specific products

This commit is contained in:
2026-07-28 12:20:17 -03:00
parent ff9d7728e8
commit 8dc80411d7
14 changed files with 355 additions and 8 deletions

View File

@@ -4,9 +4,11 @@ namespace App\Domains\Catalog\Controllers;
use App\Domains\Catalog\Enums\GroupLayout;
use App\Domains\Catalog\Models\CatalogItem;
use App\Domains\Catalog\Models\Category;
use App\Domains\Catalog\Models\FeaturedGroup;
use App\Domains\Catalog\Models\FeaturedItem;
use App\Domains\Catalog\Requests\CatalogItemDetailRequest;
use App\Domains\Catalog\Requests\CategoryPageRequest;
use App\Domains\Catalog\Requests\FeaturedGroupPageRequest;
use App\Domains\Catalog\Requests\SearchCatalogItemsRequest;
use App\Domains\Catalog\Requests\StoreCatalogItemRequest;
@@ -57,6 +59,32 @@ class CatalogController extends Controller
);
}
public function category(
CategoryPageRequest $request,
Tenant $tenant,
Category $category,
CatalogService $catalogService,
): AnonymousResourceCollection {
abort_unless($category->tenant_code === $tenant->codigo, 404);
return CatalogSearchItemResource::collection(
$catalogService->categoryItems(
$tenant,
$category,
$tenant->search_items_per_page,
(int) $request->validated('page', 1),
)
)->additional([
'category' => [
'id' => $category->id,
'nombre' => $category->nombre,
'categoria_id' => $category->categoria_id,
],
'layout' => $tenant->search_product_layout->value,
'group_layout' => $tenant->search_group_layout->value,
]);
}
public function featuredGroupItems(
FeaturedGroupPageRequest $request,
Tenant $tenant,

View File

@@ -0,0 +1,21 @@
<?php
namespace App\Domains\Catalog\Requests;
use Illuminate\Foundation\Http\FormRequest;
class CategoryPageRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
/** @return array<string, list<string>> */
public function rules(): array
{
return [
'page' => ['sometimes', 'integer', 'min:1'],
];
}
}

View File

@@ -7,6 +7,7 @@ use App\Domains\Attachable\Services\AttachmentService;
use App\Domains\Catalog\Enums\CatalogItemType;
use App\Domains\Catalog\Models\Attribute;
use App\Domains\Catalog\Models\CatalogItem;
use App\Domains\Catalog\Models\Category;
use App\Domains\Catalog\Models\Inventory;
use App\Domains\Catalog\Models\ItemAttribute;
use App\Domains\Catalog\Models\Variant;
@@ -210,6 +211,29 @@ class CatalogService
]));
}
/** @return LengthAwarePaginator<CatalogItem> */
public function categoryItems(
Tenant $tenant,
Category $category,
int $perPage,
int $page,
): LengthAwarePaginator {
return CatalogItem::query()
->where('tenant_code', $tenant->codigo)
->where('category_id', $category->id)
->with([
'attachments',
'inventory',
'variants.inventory',
'variants.attachments',
'variants.definitions.itemAttribute.attribute',
'bundleComponents.catalogItem',
'bundleComponents.variant.catalogItem',
])
->orderBy('nombre')
->paginate(perPage: $perPage, pageName: 'page', page: $page);
}
public function delete(CatalogItem $catalogItem): void
{
DB::transaction(function () use ($catalogItem): void {

View File

@@ -9,6 +9,8 @@ Route::prefix('tenants/{tenant:codigo}')->group(function (): void {
->name('catalog.featured-groups.items.index');
Route::get('catalog-items', [CatalogController::class, 'search'])
->name('catalog-items.index');
Route::get('categories/{category}', [CatalogController::class, 'category'])
->name('categories.show');
Route::get('catalog-items/{catalogItem}', [CatalogController::class, 'show']);
Route::post('catalog-items', [CatalogController::class, 'store']);
});

View File

@@ -16,7 +16,14 @@ class BootstrapTenantController extends Controller
return TenantResource::make(
Tenant::query()
->with(['headerLogo', 'footerLogo', 'mainCarouselImages', 'menues', 'socialMedia'])
->with([
'headerLogo',
'footerLogo',
'mainCarouselImages',
'menues',
'socialMedia',
'categories' => fn ($query) => $query->orderBy('nombre'),
])
->where('dominio', $dominio)
->firstOrFail()
);

View File

@@ -6,6 +6,7 @@ use App\Domains\Attachable\Models\Attachment;
use App\Domains\Catalog\Enums\GroupLayout;
use App\Domains\Catalog\Enums\ProductLayout;
use App\Domains\Catalog\Models\CatalogItem;
use App\Domains\Catalog\Models\Category;
use App\Domains\Menu\Models\Menu;
use App\Domains\Menu\Models\TenantMenu;
use Illuminate\Database\Eloquent\Attributes\Fillable;
@@ -109,6 +110,14 @@ class Tenant extends Model
return $this->hasMany(CatalogItem::class, 'tenant_code', 'codigo');
}
/**
* @return HasMany<Category, $this>
*/
public function categories(): HasMany
{
return $this->hasMany(Category::class, 'tenant_code', 'codigo');
}
/**
* @return BelongsToMany<SocialMedia, $this>
*/

View File

@@ -2,6 +2,7 @@
namespace App\Domains\Tenant\Resources;
use App\Domains\Catalog\Models\Category;
use App\Domains\Menu\Models\Menu;
use App\Domains\Tenant\Models\Tenant;
use Illuminate\Http\Request;
@@ -64,9 +65,43 @@ class TenantResource extends JsonResource
'menues',
fn () => $this->menuTree($this->menues)
),
'categories' => $this->whenLoaded(
'categories',
fn () => $this->categoryTree($this->categories)
),
];
}
/**
* @param Collection<int, Category> $categories
* @return Collection<int, array<string, mixed>>
*/
private function categoryTree(Collection $categories): Collection
{
$categoryIds = $categories->pluck('id')->flip();
$childrenByParent = $categories
->filter(fn (Category $category) => $category->categoria_id !== null
&& $categoryIds->has($category->categoria_id))
->groupBy('categoria_id');
$formatCategory = function (Category $category) use (&$formatCategory, $childrenByParent): array {
return [
'id' => $category->id,
'nombre' => $category->nombre,
'subcategories' => $childrenByParent
->get($category->id, collect())
->map($formatCategory)
->values(),
];
};
return $categories
->filter(fn (Category $category) => $category->categoria_id === null
|| ! $categoryIds->has($category->categoria_id))
->map($formatCategory)
->values();
}
/**
* @param Collection<int, Menu> $menus
* @return Collection<int, array<string, mixed>>