поиск+доставка

This commit is contained in:
Alisa
2026-06-07 13:12:42 +03:00
parent 65d182b4af
commit ee753429e2
20 changed files with 3429 additions and 91 deletions
+91 -3
View File
@@ -9,13 +9,25 @@ use App\Repositories\ProductRepository;
final class CatalogController
{
private ?string $searchBasePath = null;
public function index(): void
{
$categoryRepository = new CategoryRepository();
$productRepository = new ProductRepository();
$pagination = $this->paginationInput();
$totalProducts = $productRepository->catalogTotal(true);
$products = $productRepository->catalogPreview($pagination['limit'], $pagination['offset'], true);
$searchQuery = trim((string) ($_GET['q'] ?? $this->queryParameter('q')));
$basePath = '/catalog';
if ($searchQuery !== '') {
$searchResult = $this->searchProducts($productRepository, $searchQuery, $pagination['limit'], $pagination['offset']);
$totalProducts = $searchResult['total'];
$products = $searchResult['products'];
$basePath = $this->searchBasePath ?? ($basePath . '?q=' . rawurlencode($searchQuery));
} else {
$totalProducts = $productRepository->catalogTotal(true);
$products = $productRepository->catalogPreview($pagination['limit'], $pagination['offset'], true);
}
view('catalog/index', [
'title' => 'Каталог Рыбсток - рыба, морепродукты, мясо, сыры и продукты',
@@ -28,10 +40,18 @@ final class CatalogController
'categories' => $categoryRepository->tree(),
'currentCategory' => null,
'products' => $products,
'pagination' => $this->paginationData('/catalog', $totalProducts, count($products), $pagination),
'searchQuery' => $searchQuery,
'pagination' => $this->paginationData($basePath, $totalProducts, count($products), $pagination),
]);
}
public function search(string $query): void
{
$_GET['q'] = trim($query);
$this->searchBasePath = '/search/' . rawurlencode(trim($query));
$this->index();
}
public function category(string $slug): void
{
$categoryRepository = new CategoryRepository();
@@ -140,6 +160,74 @@ final class CatalogController
];
}
private function queryParameter(string $name): string
{
$query = (string) (parse_url($_SERVER['REQUEST_URI'] ?? '', PHP_URL_QUERY) ?? '');
if ($query === '') {
return '';
}
parse_str($query, $parameters);
return (string) ($parameters[$name] ?? '');
}
/**
* @return array{products: array<int, array<string, mixed>>, total: int}
*/
private function searchProducts(ProductRepository $productRepository, string $query, int $limit, int $offset): array
{
if (method_exists($productRepository, 'searchCatalog')) {
return $productRepository->searchCatalog($query, $limit, $offset, true);
}
$needle = $this->lower($query);
$words = array_values(array_filter(preg_split('/\s+/u', $needle) ?: []));
$ranked = [];
foreach ($productRepository->catalogPreview(5000, 0, true) as $product) {
$haystack = $this->lower(implode(' ', [
(string) ($product['name'] ?? ''),
(string) ($product['short_description'] ?? ''),
(string) ($product['category_name'] ?? ''),
(string) ($product['category_display_name'] ?? ''),
]));
$score = 0;
if ($needle !== '' && str_contains($haystack, $needle)) {
$score += 100;
}
foreach ($words as $word) {
if ($word !== '' && str_contains($haystack, $word)) {
$score += 20;
}
}
if ($score > 0) {
$product['_search_score'] = $score;
$ranked[] = $product;
}
}
usort($ranked, static fn (array $left, array $right): int => ($right['_search_score'] ?? 0) <=> ($left['_search_score'] ?? 0));
foreach ($ranked as &$product) {
unset($product['_search_score']);
}
unset($product);
return [
'products' => array_slice($ranked, $offset, $limit),
'total' => count($ranked),
];
}
private function lower(string $value): string
{
return function_exists('mb_strtolower') ? mb_strtolower($value, 'UTF-8') : strtolower($value);
}
/**
* @param array{page:int, limit:int, offset:int, per_page:int, cumulative:bool} $input
* @return array<string, mixed>