196 lines
8.1 KiB
PHP
196 lines
8.1 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace Lts\SearchAutocomplete\Controller\Ajax;
|
|
|
|
use Magento\Catalog\Helper\ImageFactory;
|
|
use Magento\Catalog\Model\Product\Visibility;
|
|
use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory as CategoryCollectionFactory;
|
|
use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory as ProductCollectionFactory;
|
|
use Magento\CatalogInventory\Api\StockRegistryInterface;
|
|
use Magento\Framework\App\Action\Action;
|
|
use Magento\Framework\App\Action\Context;
|
|
use Magento\Framework\App\Action\HttpGetActionInterface;
|
|
use Magento\Framework\Controller\Result\Json;
|
|
use Magento\Framework\Controller\Result\JsonFactory;
|
|
use Magento\Framework\Pricing\PriceCurrencyInterface;
|
|
use Magento\Search\Model\ResourceModel\Query\CollectionFactory as QueryCollectionFactory;
|
|
use Magento\Store\Model\StoreManagerInterface;
|
|
|
|
class Suggest extends Action implements HttpGetActionInterface
|
|
{
|
|
private JsonFactory $jsonFactory;
|
|
private ProductCollectionFactory $productCollectionFactory;
|
|
private CategoryCollectionFactory $categoryCollectionFactory;
|
|
private QueryCollectionFactory $queryCollectionFactory;
|
|
private StoreManagerInterface $storeManager;
|
|
private Visibility $productVisibility;
|
|
private ImageFactory $imageFactory;
|
|
private PriceCurrencyInterface $priceCurrency;
|
|
private StockRegistryInterface $stockRegistry;
|
|
|
|
public function __construct(
|
|
Context $context,
|
|
JsonFactory $jsonFactory,
|
|
ProductCollectionFactory $productCollectionFactory,
|
|
CategoryCollectionFactory $categoryCollectionFactory,
|
|
QueryCollectionFactory $queryCollectionFactory,
|
|
StoreManagerInterface $storeManager,
|
|
Visibility $productVisibility,
|
|
ImageFactory $imageFactory,
|
|
PriceCurrencyInterface $priceCurrency,
|
|
StockRegistryInterface $stockRegistry
|
|
) {
|
|
parent::__construct($context);
|
|
$this->jsonFactory = $jsonFactory;
|
|
$this->productCollectionFactory = $productCollectionFactory;
|
|
$this->categoryCollectionFactory = $categoryCollectionFactory;
|
|
$this->queryCollectionFactory = $queryCollectionFactory;
|
|
$this->storeManager = $storeManager;
|
|
$this->productVisibility = $productVisibility;
|
|
$this->imageFactory = $imageFactory;
|
|
$this->priceCurrency = $priceCurrency;
|
|
$this->stockRegistry = $stockRegistry;
|
|
}
|
|
|
|
public function execute(): Json
|
|
{
|
|
$result = $this->jsonFactory->create();
|
|
$term = trim((string)$this->getRequest()->getParam('q'));
|
|
|
|
if (mb_strlen($term) < 2) {
|
|
return $result->setData(['suggestions' => [], 'products' => [], 'total' => 0]);
|
|
}
|
|
|
|
$store = $this->storeManager->getStore();
|
|
$like = '%' . addcslashes($term, '%_') . '%';
|
|
$products = $this->productCollectionFactory->create();
|
|
$products->setStore($store)
|
|
->addStoreFilter($store)
|
|
->setVisibility($this->productVisibility->getVisibleInSiteIds())
|
|
->addAttributeToSelect(['name', 'sku', 'small_image', 'thumbnail', 'price', 'special_price'])
|
|
->addAttributeToFilter([
|
|
['attribute' => 'name', 'like' => $like],
|
|
['attribute' => 'sku', 'like' => $like]
|
|
], null, 'left')
|
|
->addAttributeToSort('name', 'ASC')
|
|
->setPageSize(5)
|
|
->setCurPage(1);
|
|
|
|
$total = (int)$products->getSize();
|
|
$productData = [];
|
|
foreach ($products as $product) {
|
|
try {
|
|
$inStock = (bool)$this->stockRegistry->getStockItem((int)$product->getId())->getIsInStock();
|
|
} catch (\Throwable $exception) {
|
|
$inStock = true;
|
|
}
|
|
|
|
$imageHelper = $this->imageFactory->create()->init($product, 'product_small_image');
|
|
$connection = $products->getConnection();
|
|
$galleryTable = $products->getTable('catalog_product_entity_media_gallery');
|
|
$linkTable = $products->getTable('catalog_product_entity_media_gallery_value_to_entity');
|
|
$imageFile = $connection->fetchOne(
|
|
$connection->select()
|
|
->from(['gallery' => $galleryTable], ['value'])
|
|
->joinInner(['link' => $linkTable], 'link.value_id = gallery.value_id', [])
|
|
->where('link.entity_id = ?', (int)$product->getId())
|
|
->order('gallery.value_id ASC')
|
|
->limit(1)
|
|
);
|
|
$imageUrl = '';
|
|
if ($imageFile) {
|
|
$localImage = BP . '/pub/media/catalog/product' . $imageFile;
|
|
if (is_file($localImage)) {
|
|
$imageHelper->setImageFile((string)$imageFile);
|
|
} else {
|
|
$imageUrl = 'https://ltsecurityinc.com/media/catalog/product' . $imageFile;
|
|
}
|
|
}
|
|
|
|
$productData[] = [
|
|
'id' => (int)$product->getId(),
|
|
'name' => (string)$product->getName(),
|
|
'sku' => (string)$product->getSku(),
|
|
'url' => (string)$product->getProductUrl(),
|
|
'image' => $imageUrl ?: $imageHelper->resize(86, 86)->getUrl(),
|
|
'price' => $this->priceCurrency->format((float)$product->getFinalPrice(), false),
|
|
'in_stock' => $inStock,
|
|
'add_url' => $this->_url->getUrl('checkout/cart/add', ['product' => (int)$product->getId()])
|
|
];
|
|
}
|
|
|
|
return $result->setData([
|
|
'suggestions' => $this->getSuggestions($term, (int)$store->getId(), $productData),
|
|
'products' => $productData,
|
|
'total' => $total,
|
|
'search_url' => $this->_url->getUrl('catalogsearch/result', ['q' => $term])
|
|
]);
|
|
}
|
|
|
|
private function getSuggestions(string $term, int $storeId, array $products): array
|
|
{
|
|
$suggestions = [];
|
|
$this->appendSuggestion($suggestions, $term);
|
|
|
|
$queries = $this->queryCollectionFactory->create();
|
|
$queries->setStoreId($storeId)->setQueryFilter($term)->setPageSize(8);
|
|
foreach ($queries as $query) {
|
|
$this->appendSuggestion($suggestions, (string)$query->getQueryText());
|
|
}
|
|
|
|
$matchedCategories = $this->categoryCollectionFactory->create();
|
|
$matchedCategories->setStoreId($storeId)
|
|
->addAttributeToSelect(['name', 'path', 'level'])
|
|
->addAttributeToFilter('is_active', 1)
|
|
->addAttributeToFilter('name', ['like' => '%' . addcslashes($term, '%_') . '%'])
|
|
->setPageSize(5);
|
|
|
|
foreach ($matchedCategories as $category) {
|
|
$this->appendSuggestion($suggestions, (string)$category->getName());
|
|
if (count($suggestions) >= 8) {
|
|
break;
|
|
}
|
|
|
|
$children = $this->categoryCollectionFactory->create();
|
|
$children->setStoreId($storeId)
|
|
->addAttributeToSelect(['name', 'level'])
|
|
->addAttributeToFilter('is_active', 1)
|
|
->addFieldToFilter('path', ['like' => '%/' . (int)$category->getId() . '/%'])
|
|
->addFieldToFilter('level', ['lteq' => (int)$category->getLevel() + 2])
|
|
->setPageSize(8);
|
|
|
|
foreach ($children as $child) {
|
|
$name = (string)$child->getName();
|
|
$label = stripos($name, $term) === false ? $term . ' ' . $name : $name;
|
|
$this->appendSuggestion($suggestions, $label);
|
|
if (count($suggestions) >= 8) {
|
|
break 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach ($products as $product) {
|
|
$this->appendSuggestion($suggestions, (string)$product['name']);
|
|
if (count($suggestions) >= 8) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return array_slice(array_values($suggestions), 0, 8);
|
|
}
|
|
|
|
private function appendSuggestion(array &$suggestions, string $label): void
|
|
{
|
|
$label = trim(preg_replace('/\s+/', ' ', $label) ?: '');
|
|
if ($label === '') {
|
|
return;
|
|
}
|
|
|
|
$key = mb_strtolower($label);
|
|
if (!isset($suggestions[$key])) {
|
|
$suggestions[$key] = ['title' => $label];
|
|
}
|
|
}
|
|
}
|