поиск+доставка
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user