100 lines
2.9 KiB
PHP
100 lines
2.9 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Http\Requests\ProductIndexRequest;
|
|
use App\Http\Resources\ProductResource;
|
|
use App\Models\Category;
|
|
use App\Models\Product;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
use Illuminate\Support\Collection;
|
|
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
|
|
|
|
class ProductController extends Controller
|
|
{
|
|
public function index(ProductIndexRequest $request): AnonymousResourceCollection
|
|
{
|
|
$query = Product::query()
|
|
->with(['category:id,name,slug', 'brand:id,name,slug'])
|
|
->active()
|
|
->whereNotNull('published_at')
|
|
->orderByDesc('published_at')
|
|
->orderByDesc('id');
|
|
|
|
$this->applySearchFilter($query, (string) ($request->validated('q') ?? ''));
|
|
$this->applyCategoryFilter($query, $request->validated('category'));
|
|
|
|
$paginator = $query->paginate(24)->appends($request->query());
|
|
|
|
return ProductResource::collection($paginator);
|
|
}
|
|
|
|
private function applySearchFilter(Builder $query, string $search): void
|
|
{
|
|
$search = trim($search);
|
|
|
|
if ($search !== '') {
|
|
$query->where(function (Builder $builder) use ($search): void {
|
|
$builder
|
|
->where('name', 'like', "%{$search}%")
|
|
->orWhere('description', 'like', "%{$search}%")
|
|
->orWhere('sku', 'like', "%{$search}%");
|
|
});
|
|
}
|
|
}
|
|
|
|
private function applyCategoryFilter(Builder $query, mixed $categorySlug): void
|
|
{
|
|
if (is_string($categorySlug) && $categorySlug !== '') {
|
|
$category = Category::query()
|
|
->where('slug', $categorySlug)
|
|
->first();
|
|
|
|
if (! $category instanceof Category) {
|
|
return;
|
|
}
|
|
|
|
$query->whereIn('category_id', $this->collectDescendantCategoryIds($category->id));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return list<int>
|
|
*/
|
|
private function collectDescendantCategoryIds(int $rootCategoryId): array
|
|
{
|
|
/** @var Collection<int, Category> $categories */
|
|
$categories = Category::query()
|
|
->select(['id', 'parent_id'])
|
|
->get();
|
|
|
|
$byParent = [];
|
|
|
|
foreach ($categories as $category) {
|
|
if ($category->parent_id === null) {
|
|
continue;
|
|
}
|
|
|
|
$byParent[$category->parent_id][] = (int) $category->id;
|
|
}
|
|
|
|
$stack = [$rootCategoryId];
|
|
$seen = [];
|
|
|
|
while ($stack !== []) {
|
|
$categoryId = array_pop($stack);
|
|
|
|
if ($categoryId === null || isset($seen[$categoryId])) {
|
|
continue;
|
|
}
|
|
|
|
$seen[$categoryId] = true;
|
|
|
|
foreach ($byParent[$categoryId] ?? [] as $childId) {
|
|
$stack[] = $childId;
|
|
}
|
|
}
|
|
|
|
return array_map('intval', array_keys($seen));
|
|
}
|
|
}
|