Your IP : 127.0.0.1
<?php
/**
* Mirasvit
*
* This source file is subject to the Mirasvit Software License, which is available at https://mirasvit.com/license/.
* Do not edit or add to this file if you wish to upgrade the to newer versions in the future.
* If you wish to customize this module for your needs.
* Please refer to http://www.magentocommerce.com for more information.
*
* @category Mirasvit
* @package mirasvit/module-search-autocomplete
* @version 1.2.4
* @copyright Copyright (C) 2020 Mirasvit (https://mirasvit.com/)
*/
namespace Mirasvit\SearchAutocomplete\Service;
use Magento\Framework\Filesystem;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Mirasvit\SearchAutocomplete\Api\Repository\IndexRepositoryInterface;
use Mirasvit\SearchAutocomplete\Model\Config;
use Magento\Search\Helper\Data as SearchHelper;
use Magento\Search\Model\ResourceModel\Query\CollectionFactory as QueryCollectionFactory;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\Url;
use Magento\Search\Model\QueryFactory;
use Magento\Framework\Message\ManagerInterface;
use Magento\Framework\Phrase\Renderer\Translate;
use Magento\Framework\Phrase;
class JsonConfigService
{
const AUTOCOMPLETE = 'autocomplete';
const TYPEAHEAD = 'typeahead';
/**
* @var Filesystem
*/
private $fs;
/**
* @var ScopeConfigInterface
*/
private $scopeConfig;
/**
* @var Config
*/
private $config;
/**
* @var IndexRepositoryInterface
*/
private $indexRepository;
/**
* @var SearchHelper
*/
private $searchHelper;
/**
* @var QueryCollectionFactory
*/
private $queryCollectionFactory;
/**
* @var StoreManagerInterface
*/
private $storeManager;
/**
* @var JsonAdvancedOptionsService
*/
private $advancedService;
/**
* @var Url
*/
private $urlBuilder;
/**
* @var ManagerInterface
*/
private $messageManager;
/**
* JsonConfigService constructor.
* @param Filesystem $fs
* @param ScopeConfigInterface $scopeConfig
* @param Config $config
* @param IndexRepositoryInterface $indexRepository
* @param SearchHelper $searchHelper
* @param QueryCollectionFactory $queryCollectionFactory
* @param StoreManagerInterface $storeManager
* @param JsonAdvancedOptionsService $advancedService
* @param Url $urlBuilder
* @param ManagerInterface $messageManager
*/
public function __construct(
Filesystem $fs,
ScopeConfigInterface $scopeConfig,
Config $config,
IndexRepositoryInterface $indexRepository,
SearchHelper $searchHelper,
QueryCollectionFactory $queryCollectionFactory,
StoreManagerInterface $storeManager,
JsonAdvancedOptionsService $advancedService,
Url $urlBuilder,
ManagerInterface $messageManager
) {
$this->fs = $fs;
$this->scopeConfig = $scopeConfig;
$this->config = $config;
$this->indexRepository = $indexRepository;
$this->searchHelper = $searchHelper;
$this->queryCollectionFactory = $queryCollectionFactory;
$this->storeManager = $storeManager;
$this->advancedService = $advancedService;
$this->urlBuilder = $urlBuilder;
$this->messageManager = $messageManager;
}
/**
* @param mixed $option
* @return $this
*/
public function ensure($option)
{
$path = $this->fs->getDirectoryRead(DirectoryList::CONFIG)->getAbsolutePath();
$filePath = $path . $option . '.json';
if (!$this->isOptionEnabled($option)) {
if (file_exists($filePath)) {
unlink($filePath);
}
return $this;
}
$config = $this->generate($option);
if ($option == self::AUTOCOMPLETE) {
$config['advancedConfig'] = $this->generateAutocompleteAdvancedConfig();
}
try {
file_put_contents($filePath, \Zend_Json::encode($config));
} catch (\LogicException $e) {
$e->getMessage();
}
$this->messageManager->addWarning('To avoid search autocomplete downtime please run search reindex.');
return $this;
}
/**
* @param mixed $option
* @return array
*/
public function generate($option)
{
switch ($option) {
case self::AUTOCOMPLETE:
return $this->generateAutocompleteConfig();
break;
case self::TYPEAHEAD:
return $this->generateTypeaheadConfig();
break;
default:
return false;
break;
}
}
/**
* @param mixed $option
* @return bool
*/
private function isOptionEnabled($option)
{
switch ($option) {
case self::AUTOCOMPLETE:
return $this->config->isFastMode();
break;
case self::TYPEAHEAD:
return $this->config->isTypeAheadEnabled();
break;
default:
return false;
break;
}
}
/**
* @return array
*/
private function generateAutocompleteConfig()
{
$config = [
'engine' => $this->scopeConfig->getValue('search/engine/engine'),
'is_optimize_mobile' => $this->config->isOptimizeMobile(),
'is_show_cart_button' => $this->config->isShowCartButton(),
'is_show_image' => $this->config->isShowImage(),
'is_show_price' => $this->config->isShowPrice(),
'is_show_rating' => $this->config->isShowRating(),
'is_show_sku' => $this->config->isShowSku(),
'is_show_short_description' => $this->config->isShowShortDescription(),
'textAll' => $this->getStoreSpecificText('Show all %d results →', null, "%d"),
'textEmpty' => $this->getStoreSpecificText('Sorry, nothing has been found for "%s".', null, "%s"),
'urlAll' => $this->getStoreSpecificAllUrls(),
];
foreach ($this->storeManager->getStores() as $store) {
$indexes = $this->indexRepository->getIndices();
foreach ($indexes as $index) {
$index->addData($this->config->getIndexOptions($index->getIdentifier()));
}
usort($indexes, function ($a, $b) {
return ($a->getOrder() < $b->getOrder()) ? -1 : 1;
});
foreach ($indexes as $index) {
$identifier = $index->getIdentifier();
if (!$this->config->getIndexOptionValue($identifier, 'is_active')) {
continue;
}
if ($identifier == 'magento_catalog_categoryproduct' || $identifier == 'magento_search_query') {
continue;
}
$index->addData($this->config->getIndexOptions($identifier));
$config['indexes'][$store->getId()][$identifier] = [
'title' => $this->getStoreSpecificText($index->getTitle(), $store->getId()),
'identifier' => $identifier,
'order' => $index->getOrder(),
'limit' => $index->getLimit(),
];
}
}
return $config;
}
/**
* @return array
*/
private function generateTypeaheadConfig()
{
$config = [];
$config['engine'] = false;
$collection = $this->queryCollectionFactory->create();
$collection->getSelect()->reset(\Zend_Db_Select::COLUMNS)
->columns([
'suggest' => new \Zend_Db_Expr('MAX(query_text)'),
'suggest_key' => new \Zend_Db_Expr('substring(query_text,1,2)'),
'popularity' => new \Zend_Db_Expr('MAX(popularity)'),
])
->where('num_results > 0')
->where('display_in_terms = 1')
->where('is_active = 1')
->where('popularity > 10 ')
->where('CHAR_LENGTH(query_text) > 3')
->group(new \Zend_Db_Expr('substring(query_text,1,6)'))
->group(new \Zend_Db_Expr('substring(query_text,1,2)'))
->order('suggest_key '. \Magento\Framework\DB\Select::SQL_ASC)
->order('popularity ' . \Magento\Framework\DB\Select::SQL_DESC);
foreach ($collection as $suggestion) {
$config[strtolower($suggestion['suggest_key'])][] = strtolower($suggestion['suggest']);
}
return $config;
}
/**
* @return array
* @throws \Magento\Framework\Exception\NoSuchEntityException
*/
private function getStoreSpecificAllUrls()
{
$result = [];
foreach ($this->storeManager->getStores() as $store) {
$storeCode = $store->getCode();
$baseUrl = $this->storeManager->getStore($store->getId())->getBaseUrl();
$allResultsUrl = $this->urlBuilder->getUrl(
'catalogsearch/result',
[
'_query' => [QueryFactory::QUERY_VAR_NAME => ''],
'_secure' => false,
'_scope' => $store->getId()
]
);
if (strrpos($allResultsUrl, $baseUrl) === false && strrpos($baseUrl, '/'. $storeCode .'/') !== false) {
$baseUrl = rtrim($baseUrl, '/');
$allResultsUrlArray = explode('/', $baseUrl) + explode('/', $allResultsUrl);
$allResultsUrl = implode('/', $allResultsUrlArray);
}
$result[$store->getId()] = $allResultsUrl;
}
return $result;
}
/**
* @return array
*/
private function generateAutocompleteAdvancedConfig()
{
$config = [
'wildcard' => $this->advancedService->getWildcardSettings(),
'locale' => $this->advancedService->getLocales(),
'wildcard_exceptions' => $this->advancedService->getWildcardExceptions(),
'replace_words' => $this->advancedService->getReplaceWords(),
'not_words' => $this->advancedService->getNotWords(),
'long_tail' => $this->advancedService->getLongTail(),
'synonyms' => $this->advancedService->getSynonyms(),
'stopwords' => $this->advancedService->getStopwords(),
];
return $config;
}
/**
* @param string $sampleText
* @param null $store
* @param null $parameter
* @return array|string
*/
private function getStoreSpecificText($sampleText, $store = null, $parameter = null)
{
Phrase::setRenderer(ObjectManager::getInstance()->get(Translate::class));
if ($store > 0) {
$result = $this->getEmulatedStoreText($sampleText, $store, $parameter);
return $result;
} else {
$result = [];
foreach ($this->storeManager->getStores() as $store) {
$result[$store->getId()] = $this->getEmulatedStoreText($sampleText, $store->getId(), $parameter);
//double run is required
$result[$store->getId()] = $this->getEmulatedStoreText($sampleText, $store->getId(), $parameter);
}
return $result;
}
}
/**
* @param string $sampleText
* @param int $storeId
* @param null $parameter
* @return string
*/
private function getEmulatedStoreText($sampleText, $storeId, $parameter = null)
{
$result = __($sampleText, $parameter)->render();
try {
$emulation = ObjectManager::getInstance()->get('Magento\Store\Model\App\Emulation');
$emulation->startEnvironmentEmulation($storeId, 'frontend', true);
$state = ObjectManager::getInstance()->get('Magento\Framework\App\State');
$state->emulateAreaCode('frontend', function (&$result, $sampleText, $parameter) {
$result = __($sampleText, $parameter)->render();
}, [&$result, $sampleText, $parameter]);
} catch (\Exception $e) {
} finally {
$emulation->stopEnvironmentEmulation();
}
return $result;
}
}