diff --git a/.gitignore b/.gitignore index 6731d97ec..4abf887f0 100755 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ dev/bin/auth.json composer.lock vendor/ .php_cs.cache +.vscode diff --git a/Block/Algolia.php b/Block/Algolia.php index bc50969ef..8ed8f8c88 100755 --- a/Block/Algolia.php +++ b/Block/Algolia.php @@ -8,7 +8,9 @@ use Algolia\AlgoliaSearch\Helper\Data as CoreHelper; use Algolia\AlgoliaSearch\Helper\Entity\CategoryHelper; use Algolia\AlgoliaSearch\Helper\Entity\ProductHelper; +use Algolia\AlgoliaSearch\Helper\Entity\SuggestionHelper; use Algolia\AlgoliaSearch\Helper\LandingPageHelper; +use Magento\Catalog\Model\Product; use Magento\Checkout\Model\Session as CheckoutSession; use Magento\Customer\Model\Context as CustomerContext; use Magento\Framework\App\ActionInterface; @@ -21,29 +23,103 @@ use Magento\Framework\Stdlib\DateTime\DateTime; use Magento\Framework\Url\Helper\Data; use Magento\Framework\View\Element\Template; +use Magento\Sales\Model\Order; use Magento\Search\Helper\Data as CatalogSearchHelper; class Algolia extends Template implements CollectionDataSourceInterface { - private $config; - private $catalogSearchHelper; - private $registry; - private $productHelper; - private $currency; - private $format; - private $algoliaHelper; - private $urlHelper; - private $formKey; - private $httpContext; - private $coreHelper; - private $categoryHelper; - private $landingPageHelper; - private $personalizationHelper; - private $checkoutSession; - private $date; - - private $priceKey; + /** + * @var ConfigHelper + */ + protected $config; + /** + * @var CatalogSearchHelper + */ + protected $catalogSearchHelper; + /** + * @var Registry + */ + protected $registry; + /** + * @var ProductHelper + */ + protected $productHelper; + /** + * @var Currency + */ + protected $currency; + /** + * @var Format + */ + protected $format; + /** + * @var AlgoliaHelper + */ + protected $algoliaHelper; + /** + * @var Data + */ + protected $urlHelper; + /** + * @var FormKey + */ + protected $formKey; + /** + * @var HttpContext + */ + protected $httpContext; + /** + * @var CoreHelper + */ + protected $coreHelper; + /** + * @var CategoryHelper + */ + protected $categoryHelper; + /** + * @var SuggestionHelper + */ + protected $suggestionHelper; + /** + * @var LandingPageHelper + */ + protected $landingPageHelper; + /** + * @var PersonalizationHelper + */ + protected $personalizationHelper; + /** + * @var CheckoutSession + */ + protected $checkoutSession; + /** + * @var DateTime + */ + protected $date; + + protected $priceKey; + /** + * @param Template\Context $context + * @param ConfigHelper $config + * @param CatalogSearchHelper $catalogSearchHelper + * @param ProductHelper $productHelper + * @param Currency $currency + * @param Format $format + * @param Registry $registry + * @param AlgoliaHelper $algoliaHelper + * @param Data $urlHelper + * @param FormKey $formKey + * @param HttpContext $httpContext + * @param CoreHelper $coreHelper + * @param CategoryHelper $categoryHelper + * @param SuggestionHelper $suggestionHelper + * @param LandingPageHelper $landingPageHelper + * @param PersonalizationHelper $personalizationHelper + * @param CheckoutSession $checkoutSession + * @param DateTime $date + * @param array $data + */ public function __construct( Template\Context $context, ConfigHelper $config, @@ -58,6 +134,7 @@ public function __construct( HttpContext $httpContext, CoreHelper $coreHelper, CategoryHelper $categoryHelper, + SuggestionHelper $suggestionHelper, LandingPageHelper $landingPageHelper, PersonalizationHelper $personalizationHelper, CheckoutSession $checkoutSession, @@ -76,6 +153,7 @@ public function __construct( $this->httpContext = $httpContext; $this->coreHelper = $coreHelper; $this->categoryHelper = $categoryHelper; + $this->suggestionHelper = $suggestionHelper; $this->landingPageHelper = $landingPageHelper; $this->personalizationHelper = $personalizationHelper; $this->checkoutSession = $checkoutSession; @@ -115,6 +193,11 @@ public function getCategoryHelper() return $this->categoryHelper; } + public function getSuggestionHelper() + { + return $this->suggestionHelper; + } + public function getCatalogSearchHelper() { return $this->catalogSearchHelper; @@ -175,13 +258,13 @@ public function getCurrentCategory() return $this->registry->registry('current_category'); } - /** @return \Magento\Catalog\Model\Product */ + /** @return Product */ public function getCurrentProduct() { return $this->registry->registry('product'); } - /** @return \Magento\Sales\Model\Order */ + /** @return Order */ public function getLastOrder() { return $this->checkoutSession->getLastRealOrder(); @@ -202,20 +285,17 @@ public function getTimestamp() return $this->date->gmtTimestamp('today midnight'); } - private function getAddToCartUrl($additional = []) + protected function getAddToCartUrl($additional = []) { $continueUrl = $this->urlHelper->getEncodedUrl($this->_urlBuilder->getCurrentUrl()); $urlParamName = ActionInterface::PARAM_NAME_URL_ENCODED; - $routeParams = [ $urlParamName => $continueUrl, '_secure' => $this->algoliaHelper->getRequest()->isSecure(), ]; - if ($additional !== []) { $routeParams = array_merge($routeParams, $additional); } - return $this->_urlBuilder->getUrl('checkout/cart/add', $routeParams); } @@ -225,7 +305,6 @@ protected function getCurrentLandingPage() if (!$landingPageId) { return null; } - return $this->landingPageHelper->getLandingPage($landingPageId); } } diff --git a/Block/Configuration.php b/Block/Configuration.php index 61d1cbd39..5cb1bebf9 100755 --- a/Block/Configuration.php +++ b/Block/Configuration.php @@ -40,6 +40,8 @@ public function getConfiguration() $categoryHelper = $this->getCategoryHelper(); + $suggestionHelper = $this->getSuggestionHelper(); + $productHelper = $this->getProductHelper(); $algoliaHelper = $this->getAlgoliaHelper(); @@ -213,6 +215,7 @@ public function getConfiguration() 'priceFormat' => $priceFormat, 'maxValuesPerFacet' => (int) $config->getMaxValuesPerFacet(), 'autofocus' => true, + 'resultPageUrl' => $this->getCatalogSearchHelper()->getResultUrl(), 'request' => [ 'query' => html_entity_decode($query), 'refinementKey' => $refinementKey, @@ -225,10 +228,10 @@ public function getConfiguration() 'showCatsNotIncludedInNavigation' => $config->showCatsNotIncludedInNavigation(), 'showSuggestionsOnNoResultsPage' => $config->showSuggestionsOnNoResultsPage(), 'baseUrl' => $baseUrl, - 'popularQueries' => $config->getPopularQueries(), + 'popularQueries' => $suggestionHelper->getPopularQueries($this->getStoreId()), 'useAdaptiveImage' => $config->useAdaptiveImage(), 'urls' => [ - 'logo' => $this->getViewFileUrl('Algolia_AlgoliaSearch::images/search-by-algolia.svg'), + 'logo' => $this->getViewFileUrl('Algolia_AlgoliaSearch::images/algolia-logo-blue.svg'), ], 'ccAnalytics' => [ 'enabled' => $config->isClickConversionAnalyticsEnabled(), @@ -305,17 +308,15 @@ public function getConfiguration() $transport = new DataObject($algoliaJsConfig); $this->_eventManager->dispatch('algolia_after_create_configuration', ['configuration' => $transport]); - $algoliaJsConfig = $transport->getData(); - - return $algoliaJsConfig; + return $transport->getData(); } - private function areCategoriesInFacets($facets) + protected function areCategoriesInFacets($facets) { return in_array('categories', array_column($facets, 'attribute')); } - private function getUrlTrackedParameters() + protected function getUrlTrackedParameters() { $urlTrackedParameters = ['query', 'attribute:*', 'index']; @@ -326,7 +327,7 @@ private function getUrlTrackedParameters() return $urlTrackedParameters; } - private function getOrderedProductIds(ConfigHelper $configHelper, Http $request) + protected function getOrderedProductIds(ConfigHelper $configHelper, Http $request) { $ids = []; @@ -349,22 +350,22 @@ private function getOrderedProductIds(ConfigHelper $configHelper, Http $request) return $ids; } - private function isLandingPage() + protected function isLandingPage() { return $this->getRequest()->getFullActionName() === 'algolia_landingpage_view'; } - private function getLandingPageId() + protected function getLandingPageId() { return $this->isLandingPage() ? $this->getCurrentLandingPage()->getId() : ''; } - private function getLandingPageQuery() + protected function getLandingPageQuery() { return $this->isLandingPage() ? $this->getCurrentLandingPage()->getQuery() : ''; } - private function getLandingPageConfiguration() + protected function getLandingPageConfiguration() { return $this->isLandingPage() ? $this->getCurrentLandingPage()->getConfiguration() : json_encode([]); } diff --git a/CHANGELOG.md b/CHANGELOG.md index d97f37bc1..9fdf3dbb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,29 @@ # CHANGE LOG +## 3.9.1 + +### UPDATES +- Refactored the Autocomplete to provide an extensible model for function-based templates by utilizing tagged template literals. The approach supports the use of RequireJS mixins for overriding the template functionality. +- Update the synonym area notice in the Magento admin to point customers to use the Algolia dashboard for Synonym management as Magento Dashboard will be deprecated in a future release +- Refactored the casting Attributes +- Managing Max record size via the admin + + +### FIXES +- Autocomplete category links not preselecting facets on the target URL +- Fixed bug related to conjunctive facets and adaptive images in autocomplete +- Fixed issue with showing __empty__ in the url if autocomplete was disabled +- Fixed autocomplete suggestions +- Instant search fixes when the price was set to retrievable = 'no' +- Price attribute fixes in autocomplete when the price attribute is set to Non-Retrievable +- Add to cart triggering duplicate view event for the Algolia recommend products +- Issues while saving and loading data by the wrong cache key for the popular queries +- Issues with max_retries in clear old jobs function in the queue +- Place Order duplicate Conversion issue for Grouped Product +- Fixes issues with store-specific category index + + + ## 3.9.0 ### New Features diff --git a/Helper/AlgoliaHelper.php b/Helper/AlgoliaHelper.php index a34ef8160..c4351fac6 100755 --- a/Helper/AlgoliaHelper.php +++ b/Helper/AlgoliaHelper.php @@ -502,8 +502,7 @@ private function prepareRecords(&$objects, $indexName) private function getMaxRecordSize() { if (!$this->maxRecordSize) { - $this->maxRecordSize = $this->config->getMaxRecordSizeLimit() - ? $this->config->getMaxRecordSizeLimit() : $this->config->getDefaultMaxRecordSize(); + $this->maxRecordSize = $this->config->getMaxRecordSizeLimit(); } return $this->maxRecordSize; @@ -611,13 +610,27 @@ private function castRecord($object) return $object; } + /** + * This method serves to prevent parse of float values that exceed PHP_FLOAT_MAX as INF will break + * JSON encoding. + * + * To further customize the handling of values that may be incorrectly interpreted as numeric by + * PHP you can implement an "after" plugin on this method. + * + * @param $value - what PHP thinks is a floating point number + * @return bool + */ + public function isValidFloat(string $value) : bool { + return floatval($value) !== INF; + } + private function castAttribute($value) { if (is_numeric($value) && floatval($value) === floatval((int) $value)) { return (int) $value; } - if (is_numeric($value)) { + if (is_numeric($value) && $this->isValidFloat($value)) { return floatval($value); } diff --git a/Helper/ConfigHelper.php b/Helper/ConfigHelper.php index 0817ff877..dfe99c39d 100755 --- a/Helper/ConfigHelper.php +++ b/Helper/ConfigHelper.php @@ -2,8 +2,8 @@ namespace Algolia\AlgoliaSearch\Helper; -use Algolia\AlgoliaSearch\Helper\Entity\SuggestionHelper; use Magento; +use Magento\Customer\Model\ResourceModel\Group\Collection as GroupCollection; use Magento\Directory\Model\Currency as DirCurrency; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\DataObject; @@ -50,7 +50,6 @@ class ConfigHelper public const INDEX_OUT_OF_STOCK_OPTIONS = 'algoliasearch_products/products/index_out_of_stock_options'; public const CATEGORY_ATTRIBUTES = 'algoliasearch_categories/categories/category_additional_attributes'; - public const INDEX_PRODUCT_COUNT = 'algoliasearch_categories/categories/index_product_count'; public const CATEGORY_CUSTOM_RANKING = 'algoliasearch_categories/categories/custom_ranking_category_attributes'; public const SHOW_CATS_NOT_INCLUDED_IN_NAV = 'algoliasearch_categories/categories/show_cats_not_included_in_navigation'; public const INDEX_EMPTY_CATEGORIES = 'algoliasearch_categories/categories/index_empty_categories'; @@ -106,9 +105,7 @@ class ConfigHelper public const EXTRA_SETTINGS_SUGGESTIONS = 'algoliasearch_extra_settings/extra_settings/suggestions_extra_settings'; public const EXTRA_SETTINGS_ADDITIONAL_SECTIONS = 'algoliasearch_extra_settings/extra_settings/additional_sections_extra_settings'; - - public const DEFAULT_MAX_RECORD_SIZE = 10000; - + public const MAGENTO_DEFAULT_CACHE_TIME = 'system/full_page_cache/ttl'; protected const IS_RECOMMEND_FREQUENTLY_BOUGHT_TOGETHER_ENABLED = 'algoliasearch_recommend/recommend/frequently_bought_together/is_frequently_bought_together_enabled'; protected const IS_RECOMMEND_RELATED_PRODUCTS_ENABLED = 'algoliasearch_recommend/recommend/related_product/is_related_products_enabled'; protected const IS_RECOMMEND_FREQUENTLY_BOUGHT_TOGETHER_ENABLED_ON_CART_PAGE = 'algoliasearch_recommend/recommend/frequently_bought_together/is_frequently_bought_together_enabled_in_cart_page'; @@ -124,35 +121,84 @@ class ConfigHelper protected const IS_TREND_ITEMS_ENABLED_IN_PDP = 'algoliasearch_recommend/recommend/trends_item/is_trending_items_enabled_on_pdp'; protected const IS_TREND_ITEMS_ENABLED_IN_SHOPPING_CART = 'algoliasearch_recommend/recommend/trends_item/is_trending_items_enabled_on_cart_page'; protected const IS_ADDTOCART_ENABLED_IN_FREQUENTLY_BOUGHT_TOGETHER = 'algoliasearch_recommend/recommend/frequently_bought_together/is_addtocart_enabled'; - protected const IS_ADDTOCART_ENABLED_IN_RELATED_PRODUCTS= 'algoliasearch_recommend/recommend/related_product/is_addtocart_enabled'; - protected const IS_ADDTOCART_ENABLED_IN_TRENDS_ITEM= 'algoliasearch_recommend/recommend/trends_item/is_addtocart_enabled'; - - private $configInterface; - private $objectManager; - private $currency; - private $storeManager; - private $dirCurrency; - private $directoryList; - private $moduleResource; - private $productMetadata; - private $eventManager; - private $currencyManager; - private $serializer; + protected const IS_ADDTOCART_ENABLED_IN_RELATED_PRODUCTS = 'algoliasearch_recommend/recommend/related_product/is_addtocart_enabled'; + protected const IS_ADDTOCART_ENABLED_IN_TRENDS_ITEM = 'algoliasearch_recommend/recommend/trends_item/is_addtocart_enabled'; + + /** + * @var Magento\Framework\App\Config\ScopeConfigInterface + */ + protected $configInterface; + + /** + * @var Currency + */ + protected $currency; + + /** + * @var StoreManagerInterface + */ + protected $storeManager; + + /** + * @var DirCurrency + */ + protected $dirCurrency; + + /** + * @var DirectoryList + */ + protected $directoryList; + + /** + * @var Magento\Framework\Module\ResourceInterface + */ + protected $moduleResource; + + /** + * @var Magento\Framework\App\ProductMetadataInterface + */ + protected $productMetadata; + + /** + * @var Magento\Framework\Event\ManagerInterface + */ + protected $eventManager; + + /** + * @var SerializerInterface + */ + protected $serializer; + + /** + * @var GroupCollection + */ + protected $groupCollection; + + /** + * @param Magento\Framework\App\Config\ScopeConfigInterface $configInterface + * @param StoreManagerInterface $storeManager + * @param Currency $currency + * @param DirCurrency $dirCurrency + * @param DirectoryList $directoryList + * @param Magento\Framework\Module\ResourceInterface $moduleResource + * @param Magento\Framework\App\ProductMetadataInterface $productMetadata + * @param Magento\Framework\Event\ManagerInterface $eventManager + * @param SerializerInterface $serializer + * @param GroupCollection $groupCollection + */ public function __construct( Magento\Framework\App\Config\ScopeConfigInterface $configInterface, - Magento\Framework\ObjectManagerInterface $objectManager, - StoreManagerInterface $storeManager, - Currency $currency, - DirCurrency $dirCurrency, - DirectoryList $directoryList, - Magento\Framework\Module\ResourceInterface $moduleResource, - Magento\Framework\App\ProductMetadataInterface $productMetadata, - Magento\Framework\Event\ManagerInterface $eventManager, - Magento\Directory\Model\Currency $currencyManager, - SerializerInterface $serializer + StoreManagerInterface $storeManager, + Currency $currency, + DirCurrency $dirCurrency, + DirectoryList $directoryList, + Magento\Framework\Module\ResourceInterface $moduleResource, + Magento\Framework\App\ProductMetadataInterface $productMetadata, + Magento\Framework\Event\ManagerInterface $eventManager, + SerializerInterface $serializer, + GroupCollection $groupCollection ) { - $this->objectManager = $objectManager; $this->configInterface = $configInterface; $this->currency = $currency; $this->storeManager = $storeManager; @@ -161,10 +207,14 @@ public function __construct( $this->moduleResource = $moduleResource; $this->productMetadata = $productMetadata; $this->eventManager = $eventManager; - $this->currencyManager = $currencyManager; $this->serializer = $serializer; + $this->groupCollection = $groupCollection; } + /** + * @param $storeId + * @return bool + */ public function indexOutOfStockOptions($storeId = null) { return $this->configInterface->isSetFlag( @@ -174,6 +224,10 @@ public function indexOutOfStockOptions($storeId = null) ); } + /** + * @param $storeId + * @return bool + */ public function showCatsNotIncludedInNavigation($storeId = null) { return $this->configInterface->isSetFlag( @@ -183,36 +237,61 @@ public function showCatsNotIncludedInNavigation($storeId = null) ); } + /** + * @param $storeId + * @return bool + */ public function shouldIndexEmptyCategories($storeId = null) { return $this->configInterface->isSetFlag(self::INDEX_EMPTY_CATEGORIES, ScopeInterface::SCOPE_STORE, $storeId); } + /** + * @return string + */ public function getMagentoVersion() { return $this->productMetadata->getVersion(); } + /** + * @return string + */ public function getMagentoEdition() { return $this->productMetadata->getEdition(); } + /** + * @return false|string + */ public function getExtensionVersion() { return $this->moduleResource->getDbVersion('Algolia_AlgoliaSearch'); } + /** + * @param $storeId + * @return bool + */ public function isDefaultSelector($storeId = null) { return '.algolia-search-input' === $this->getAutocompleteSelector($storeId); } + /** + * @param $storeId + * @return mixed + */ public function getAutocompleteSelector($storeId = null) { return $this->configInterface->getValue(self::AUTOCOMPLETE_SELECTOR, ScopeInterface::SCOPE_STORE, $storeId); } + /** + * @param $storeId + * @return mixed + */ public function indexProductOnCategoryProductsUpdate($storeId = null) { return $this->configInterface->getValue( @@ -222,67 +301,94 @@ public function indexProductOnCategoryProductsUpdate($storeId = null) ); } + /** + * @param $storeId + * @return int + */ public function getNumberOfQueriesSuggestions($storeId = null) { - return (int) $this->configInterface->getValue( + return (int)$this->configInterface->getValue( self::NB_OF_QUERIES_SUGGESTIONS, ScopeInterface::SCOPE_STORE, $storeId ); } + /** + * @param $storeId + * @return int + */ public function getNumberOfProductsSuggestions($storeId = null) { - return (int) $this->configInterface->getValue( + return (int)$this->configInterface->getValue( self::NB_OF_PRODUCTS_SUGGESTIONS, ScopeInterface::SCOPE_STORE, $storeId ); } + /** + * @param $storeId + * @return int + */ public function getNumberOfCategoriesSuggestions($storeId = null) { - return (int) $this->configInterface->getValue( + return (int)$this->configInterface->getValue( self::NB_OF_CATEGORIES_SUGGESTIONS, ScopeInterface::SCOPE_STORE, $storeId ); } - public function showSuggestionsOnNoResultsPage($storeId = null) - { - return $this->configInterface->isSetFlag( - self::SHOW_SUGGESTIONS_NO_RESULTS, - ScopeInterface::SCOPE_STORE, - $storeId - ); - } - + /** + * @param $storeId + * @return bool + */ public function isEnabledFrontEnd($storeId = null) { return $this->configInterface->isSetFlag(self::ENABLE_FRONTEND, ScopeInterface::SCOPE_STORE, $storeId); } + /** + * @param $storeId + * @return bool + */ public function isEnabledBackend($storeId = null) { return $this->configInterface->isSetFlag(self::ENABLE_BACKEND, ScopeInterface::SCOPE_STORE, $storeId); } + /** + * @param $storeId + * @return bool + */ public function makeSeoRequest($storeId = null) { return $this->configInterface->isSetFlag(self::MAKE_SEO_REQUEST, ScopeInterface::SCOPE_STORE, $storeId); } + /** + * @param $storeId + * @return bool + */ public function isLoggingEnabled($storeId = null) { return $this->configInterface->isSetFlag(self::LOGGING_ENABLED, ScopeInterface::SCOPE_STORE, $storeId); } + /** + * @param $storeId + * @return bool + */ public function getShowOutOfStock($storeId = null) { return $this->configInterface->isSetFlag(self::SHOW_OUT_OF_STOCK, ScopeInterface::SCOPE_STORE, $storeId); } + /** + * @param $storeId + * @return bool + */ public function useSecureUrlsInFrontend($storeId = null) { return $this->configInterface->isSetFlag( @@ -292,6 +398,10 @@ public function useSecureUrlsInFrontend($storeId = null) ); } + /** + * @param $storeId + * @return int + */ public function getImageWidth($storeId = null) { $imageWidth = $this->configInterface->getValue( @@ -304,9 +414,13 @@ public function getImageWidth($storeId = null) return 265; } - return (int) $imageWidth; + return (int)$imageWidth; } + /** + * @param $storeId + * @return int + */ public function getImageHeight($storeId = null) { $imageHeight = $this->configInterface->getValue( @@ -319,29 +433,40 @@ public function getImageHeight($storeId = null) return 265; } - return (int) $imageHeight; + return (int)$imageHeight; } + /** + * @param $storeId + * @return mixed + */ public function getImageType($storeId = null) { return $this->configInterface->getValue(self::XML_PATH_IMAGE_TYPE, ScopeInterface::SCOPE_STORE, $storeId); } - public function isCustomerGroupsEnabled($storeId = null) - { - return $this->configInterface->isSetFlag(self::CUSTOMER_GROUPS_ENABLE, ScopeInterface::SCOPE_STORE, $storeId); - } - + /** + * @param $storeId + * @return bool + */ public function shouldRemovePubDirectory($storeId = null) { return $this->configInterface->isSetFlag(self::REMOVE_PUB_DIR_IN_URL, ScopeInterface::SCOPE_STORE, $storeId); } + /** + * @param $storeId + * @return bool + */ public function isPartialUpdateEnabled($storeId = null) { return $this->configInterface->isSetFlag(self::PARTIAL_UPDATES, ScopeInterface::SCOPE_STORE, $storeId); } + /** + * @param $storeId + * @return array + */ public function getAutocompleteSections($storeId = null) { $attrs = $this->unserialize($this->configInterface->getValue( @@ -357,16 +482,44 @@ public function getAutocompleteSections($storeId = null) return []; } + /** + * @param $value + * @return array|bool|float|int|mixed|string|null + */ + protected function unserialize($value) + { + if (false === $value || null === $value || '' === $value) { + return false; + } + $unserialized = json_decode($value, true); + if (json_last_error() === JSON_ERROR_NONE) { + return $unserialized; + } + return $this->serializer->unserialize($value); + } + + /** + * @param $storeId + * @return int + */ public function getMinPopularity($storeId = null) { - return (int) $this->configInterface->getValue(self::MIN_POPULARITY, ScopeInterface::SCOPE_STORE, $storeId); + return (int)$this->configInterface->getValue(self::MIN_POPULARITY, ScopeInterface::SCOPE_STORE, $storeId); } + /** + * @param $storeId + * @return int + */ public function getMinNumberOfResults($storeId = null) { - return (int) $this->configInterface->getValue(self::MIN_NUMBER_OF_RESULTS, ScopeInterface::SCOPE_STORE, $storeId); + return (int)$this->configInterface->getValue(self::MIN_NUMBER_OF_RESULTS, ScopeInterface::SCOPE_STORE, $storeId); } + /** + * @param $storeId + * @return bool + */ public function isAddToCartEnable($storeId = null) { return $this->configInterface->isSetFlag( @@ -376,77 +529,124 @@ public function isAddToCartEnable($storeId = null) ); } + /** + * @param $storeId + * @return bool + */ public function isInfiniteScrollEnabled($storeId = null) { return $this->isInstantEnabled($storeId) && $this->configInterface->isSetFlag(self::INFINITE_SCROLL_ENABLE, ScopeInterface::SCOPE_STORE, $storeId); } + /** + * @param $storeId + * @return bool + */ + public function isInstantEnabled($storeId = null) + { + return $this->configInterface->isSetFlag(self::IS_INSTANT_ENABLED, ScopeInterface::SCOPE_STORE, $storeId); + } + + /** + * @param $storeId + * @return bool + */ public function isRemoveBranding($storeId = null) { return $this->configInterface->isSetFlag(self::REMOVE_BRANDING, ScopeInterface::SCOPE_STORE, $storeId); } + /** + * @param $storeId + * @return int + */ public function getMaxValuesPerFacet($storeId = null) { - return (int) $this->configInterface->getValue(self::MAX_VALUES_PER_FACET, ScopeInterface::SCOPE_STORE, $storeId); + return (int)$this->configInterface->getValue(self::MAX_VALUES_PER_FACET, ScopeInterface::SCOPE_STORE, $storeId); } + /** + * @param $storeId + * @return int + */ public function getNumberOfElementByPage($storeId = null) { - return (int) $this->configInterface->getValue(self::NUMBER_OF_ELEMENT_BY_PAGE, ScopeInterface::SCOPE_STORE, $storeId); + return (int)$this->configInterface->getValue(self::NUMBER_OF_ELEMENT_BY_PAGE, ScopeInterface::SCOPE_STORE, $storeId); } + /** + * @param $storeId + * @return mixed + */ public function getNumberOfJobToRun($storeId = null) { - $nbJobs = (int) $this->configInterface->getValue(self::NUMBER_OF_JOB_TO_RUN, ScopeInterface::SCOPE_STORE, $storeId); + $nbJobs = (int)$this->configInterface->getValue(self::NUMBER_OF_JOB_TO_RUN, ScopeInterface::SCOPE_STORE, $storeId); return max($nbJobs, 1); } + /** + * @param $storeId + * @return int + */ public function getRetryLimit($storeId = null) { - return (int) $this->configInterface->getValue(self::RETRY_LIMIT, ScopeInterface::SCOPE_STORE, $storeId); + return (int)$this->configInterface->getValue(self::RETRY_LIMIT, ScopeInterface::SCOPE_STORE, $storeId); } + /** + * @param $storeId + * @return bool + */ public function isQueueActive($storeId = null) { return $this->configInterface->isSetFlag(self::IS_ACTIVE, ScopeInterface::SCOPE_STORE, $storeId); } + /** + * @param $storeId + * @return mixed + */ public function getRemoveWordsIfNoResult($storeId = null) { return $this->configInterface->getValue(self::REMOVE_IF_NO_RESULT, ScopeInterface::SCOPE_STORE, $storeId); } + /** + * @param $storeId + * @return int + */ public function getNumberOfProductResults($storeId = null) { - return (int) $this->configInterface->getValue( + return (int)$this->configInterface->getValue( self::NUMBER_OF_PRODUCT_RESULTS, ScopeInterface::SCOPE_STORE, $storeId ); } + /** + * @param $storeId + * @return bool + */ public function replaceCategories($storeId = null) { return $this->configInterface->isSetFlag(self::REPLACE_CATEGORIES, ScopeInterface::SCOPE_STORE, $storeId); } + /** + * @param $storeId + * @return bool + */ public function isAutoCompleteEnabled($storeId = null) { return $this->configInterface->isSetFlag(self::IS_POPUP_ENABLED, ScopeInterface::SCOPE_STORE, $storeId); } - public function isInstantEnabled($storeId = null) - { - return $this->configInterface->isSetFlag(self::IS_INSTANT_ENABLED, ScopeInterface::SCOPE_STORE, $storeId); - } - /** - * @param int $storeId - * - * @return int + * @param $storeId + * @return bool */ public function isRecommendFrequentlyBroughtTogetherEnabled($storeId = null) { @@ -454,9 +654,8 @@ public function isRecommendFrequentlyBroughtTogetherEnabled($storeId = null) } /** - * @param int $storeId - * - * @return int + * @param $storeId + * @return bool */ public function isRecommendRelatedProductsEnabled($storeId = null) { @@ -464,9 +663,8 @@ public function isRecommendRelatedProductsEnabled($storeId = null) } /** - * @param int $storeId - * - * @return int + * @param $storeId + * @return bool */ public function isRecommendFrequentlyBroughtTogetherEnabledOnCartPage($storeId = null) { @@ -474,9 +672,8 @@ public function isRecommendFrequentlyBroughtTogetherEnabledOnCartPage($storeId = } /** - * @param int $storeId - * - * @return int + * @param $storeId + * @return bool */ public function isRecommendRelatedProductsEnabledOnCartPage($storeId = null) { @@ -484,9 +681,8 @@ public function isRecommendRelatedProductsEnabledOnCartPage($storeId = null) } /** - * @param int $storeId - * - * @return int + * @param $storeId + * @return bool */ public function isRemoveCoreRelatedProductsBlock($storeId = null) { @@ -494,9 +690,8 @@ public function isRemoveCoreRelatedProductsBlock($storeId = null) } /** - * @param int $storeId - * - * @return int + * @param $storeId + * @return bool */ public function isRemoveUpsellProductsBlock($storeId = null) { @@ -504,13 +699,12 @@ public function isRemoveUpsellProductsBlock($storeId = null) } /** - * @param int $storeId - * + * @param $storeId * @return int */ public function getNumberOfRelatedProducts($storeId = null) { - return (int) $this->configInterface->getValue( + return (int)$this->configInterface->getValue( self::NUM_OF_RECOMMEND_RELATED_PRODUCTS, ScopeInterface::SCOPE_STORE, $storeId @@ -518,27 +712,25 @@ public function getNumberOfRelatedProducts($storeId = null) } /** - * @param int $storeId - * + * @param $storeId * @return int */ public function getNumberOfFrequentlyBoughtTogetherProducts($storeId = null) { - return (int) $this->configInterface->getValue( + return (int)$this->configInterface->getValue( self::NUM_OF_RECOMMEND_FREQUENTLY_BOUGHT_TOGETHER_PRODUCTS, ScopeInterface::SCOPE_STORE, $storeId ); } - + /** * @param int $storeId - * * @return int */ public function isRecommendTrendingItemsEnabled($storeId = null) { - return (int) $this->configInterface->getValue( + return (int)$this->configInterface->getValue( self::IS_RECOMMEND_TRENDING_ITEMS_ENABLED, ScopeInterface::SCOPE_STORE, $storeId @@ -546,24 +738,21 @@ public function isRecommendTrendingItemsEnabled($storeId = null) } /** - * @param int $storeId - * + * @param $storeId * @return int */ public function getNumberOfTrendingItems($storeId = null) { - return (int) $this->configInterface->getValue( + return (int)$this->configInterface->getValue( self::NUM_OF_TRENDING_ITEMS, ScopeInterface::SCOPE_STORE, $storeId ); } - /** - * @param int $storeId - * - * @return string + * @param $storeId + * @return mixed */ public function getTrendingItemsFacetName($storeId = null) { @@ -575,9 +764,8 @@ public function getTrendingItemsFacetName($storeId = null) } /** - * @param int $storeId - * - * @return string + * @param $storeId + * @return mixed */ public function getTrendingItemsFacetValue($storeId = null) { @@ -589,13 +777,12 @@ public function getTrendingItemsFacetValue($storeId = null) } /** - * @param int $storeId - * + * @param $storeId * @return int */ public function isTrendItemsEnabledInPDP($storeId = null) { - return (int) $this->configInterface->getValue( + return (int)$this->configInterface->getValue( self::IS_TREND_ITEMS_ENABLED_IN_PDP, ScopeInterface::SCOPE_STORE, $storeId @@ -603,13 +790,12 @@ public function isTrendItemsEnabledInPDP($storeId = null) } /** - * @param int $storeId - * + * @param $storeId * @return int */ public function isTrendItemsEnabledInShoppingCart($storeId = null) { - return (int) $this->configInterface->getValue( + return (int)$this->configInterface->getValue( self::IS_TREND_ITEMS_ENABLED_IN_SHOPPING_CART, ScopeInterface::SCOPE_STORE, $storeId @@ -617,9 +803,8 @@ public function isTrendItemsEnabledInShoppingCart($storeId = null) } /** - * @param int $storeId - * - * @return int + * @param $storeId + * @return bool */ public function isAddToCartEnabledInFrequentlyBoughtTogether($storeId = null) { @@ -627,9 +812,8 @@ public function isAddToCartEnabledInFrequentlyBoughtTogether($storeId = null) } /** - * @param int $storeId - * - * @return int + * @param $storeId + * @return bool */ public function isAddToCartEnabledInRelatedProducts($storeId = null) { @@ -637,25 +821,36 @@ public function isAddToCartEnabledInRelatedProducts($storeId = null) } /** - * @param int $storeId - * - * @return int + * @param $storeId + * @return bool */ public function isAddToCartEnabledInTrendsItem($storeId = null) { return $this->configInterface->isSetFlag(self::IS_ADDTOCART_ENABLED_IN_TRENDS_ITEM, ScopeInterface::SCOPE_STORE, $storeId); } + /** + * @param $storeId + * @return bool + */ public function useAdaptiveImage($storeId = null) { return $this->configInterface->isSetFlag(self::USE_ADAPTIVE_IMAGE, ScopeInterface::SCOPE_STORE, $storeId); } + /** + * @param $storeId + * @return mixed + */ public function getInstantSelector($storeId = null) { return $this->configInterface->getValue(self::INSTANT_SELECTOR, ScopeInterface::SCOPE_STORE, $storeId); } + /** + * @param $storeId + * @return array + */ public function getExcludedPages($storeId = null) { $attrs = $this->unserialize($this->configInterface->getValue( @@ -663,14 +858,16 @@ public function getExcludedPages($storeId = null) ScopeInterface::SCOPE_STORE, $storeId )); - if (is_array($attrs)) { return $attrs; } - return []; } + /** + * @param $storeId + * @return mixed + */ public function getRenderTemplateDirectives($storeId = null) { return $this->configInterface->getValue( @@ -680,62 +877,49 @@ public function getRenderTemplateDirectives($storeId = null) ); } + /** + * @param $storeId + * @return bool + */ public function isAutocompleteDebugEnabled($storeId = null) { return $this->configInterface->isSetFlag(self::AUTOCOMPLETE_MENU_DEBUG, ScopeInterface::SCOPE_STORE, $storeId); } - public function getRawSortingValue($storeId = null) - { - return $this->configInterface->getValue( - self::SORTING_INDICES, - ScopeInterface::SCOPE_STORE, - $storeId - ); - } - - public function getSorting($storeId = null) - { - return $this->unserialize($this->getRawSortingValue($storeId)); - } - + /** + * @param $originalIndexName + * @param $storeId + * @param $currentCustomerGroupId + * @return array + * @throws Magento\Framework\Exception\NoSuchEntityException + */ public function getSortingIndices($originalIndexName, $storeId = null, $currentCustomerGroupId = null) { $attrs = $this->getSorting($storeId); - $currency = $this->getCurrencyCode($storeId); $attributesToAdd = []; - foreach ($attrs as $key => $attr) { $indexName = false; $sortAttribute = false; - if ($this->isCustomerGroupsEnabled($storeId) && $attr['attribute'] === 'price') { - /** @var Magento\Customer\Model\ResourceModel\Group\Collection $groupCollection */ - $groupCollection = $this->objectManager->get('Magento\Customer\Model\ResourceModel\Group\Collection'); - + $groupCollection = $this->groupCollection; if (!is_null($currentCustomerGroupId)) { $groupCollection->addFilter('customer_group_id', $currentCustomerGroupId); } - foreach ($groupCollection as $group) { - $customerGroupId = (int) $group->getData('customer_group_id'); + $customerGroupId = (int)$group->getData('customer_group_id'); $groupIndexNameSuffix = 'group_' . $customerGroupId; - $groupIndexName = $originalIndexName . '_' . $attr['attribute'] . '_' . $groupIndexNameSuffix . '_' . $attr['sort']; $groupSortAttribute = $attr['attribute'] . '.' . $currency . '.' . $groupIndexNameSuffix; - $newAttr = []; $newAttr['name'] = $groupIndexName; $newAttr['attribute'] = $attr['attribute']; $newAttr['sort'] = $attr['sort']; $newAttr['sortLabel'] = $attr['sortLabel']; - if (!array_key_exists('label', $newAttr) && array_key_exists('sortLabel', $newAttr)) { $newAttr['label'] = $newAttr['sortLabel']; } - $newAttr['ranking'] = [ $newAttr['sort'] . '(' . $groupSortAttribute . ')', 'typo', @@ -747,7 +931,6 @@ public function getSortingIndices($originalIndexName, $storeId = null, $currentC 'exact', 'custom', ]; - $attributesToAdd[$newAttr['sort']][] = $newAttr; } } elseif ($attr['attribute'] === 'price') { @@ -757,14 +940,11 @@ public function getSortingIndices($originalIndexName, $storeId = null, $currentC $indexName = $originalIndexName . '_' . $attr['attribute'] . '_' . $attr['sort']; $sortAttribute = $attr['attribute']; } - if ($indexName && $sortAttribute) { $attrs[$key]['name'] = $indexName; - if (!array_key_exists('label', $attrs[$key]) && array_key_exists('sortLabel', $attrs[$key])) { $attrs[$key]['label'] = $attrs[$key]['sortLabel']; } - $attrs[$key]['ranking'] = [ $attr['sort'] . '(' . $sortAttribute . ')', 'typo', @@ -778,9 +958,7 @@ public function getSortingIndices($originalIndexName, $storeId = null, $currentC ]; } } - $attrsToReturn = []; - if (count($attributesToAdd) > 0) { foreach ($attrs as $key => $attr) { if ($attr['attribute'] == 'price' && isset($attributesToAdd[$attr['sort']])) { @@ -790,33 +968,62 @@ public function getSortingIndices($originalIndexName, $storeId = null, $currentC } } } - if (count($attrsToReturn) > 0) { return $attrsToReturn; } - if (is_array($attrs)) { return $attrs; } - return []; } - public function getApplicationID($storeId = null) + /*** + * @param $storeId + * @return array|bool|float|int|mixed|string|null + */ + public function getSorting($storeId = null) { - return $this->configInterface->getValue(self::APPLICATION_ID, ScopeInterface::SCOPE_STORE, $storeId); + return $this->unserialize($this->getRawSortingValue($storeId)); } - public function getAPIKey($storeId = null) - { - return $this->configInterface->getValue(self::API_KEY, ScopeInterface::SCOPE_STORE, $storeId); + /** + * @param $storeId + * @return mixed + */ + public function getRawSortingValue($storeId = null) + { + return $this->configInterface->getValue( + self::SORTING_INDICES, + ScopeInterface::SCOPE_STORE, + $storeId + ); } - public function getSearchOnlyAPIKey($storeId = null) + /** + * @param $storeId + * @return string + * @throws Magento\Framework\Exception\NoSuchEntityException + */ + public function getCurrencyCode($storeId = null) { - return $this->configInterface->getValue(self::SEARCH_ONLY_API_KEY, ScopeInterface::SCOPE_STORE, $storeId); + /** @var Magento\Store\Model\Store $store */ + $store = $this->storeManager->getStore($storeId); + return $store->getCurrentCurrencyCode(); + } + + /** + * @param $storeId + * @return bool + */ + public function isCustomerGroupsEnabled($storeId = null) + { + return $this->configInterface->isSetFlag(self::CUSTOMER_GROUPS_ENABLE, ScopeInterface::SCOPE_STORE, $storeId); } + /** + * @param $storeId + * @return bool + */ public function credentialsAreConfigured($storeId = null) { return $this->getApplicationID($storeId) && @@ -824,79 +1031,46 @@ public function credentialsAreConfigured($storeId = null) $this->getSearchOnlyAPIKey($storeId); } - public function getIndexPrefix($storeId = null) + /** + * @param $storeId + * @return mixed' + */ + public function getApplicationID($storeId = null) { - return $this->configInterface->getValue(self::INDEX_PREFIX, ScopeInterface::SCOPE_STORE, $storeId); + return $this->configInterface->getValue(self::APPLICATION_ID, ScopeInterface::SCOPE_STORE, $storeId); } - public function getCategoryAdditionalAttributes($storeId = null) + /** + * @param $storeId + * @return mixed + */ + public function getAPIKey($storeId = null) { - $attributes = $this->unserialize($this->configInterface->getValue( - self::CATEGORY_ATTRIBUTES, - ScopeInterface::SCOPE_STORE, - $storeId - )); - - $customRankings = $this->unserialize($this->configInterface->getValue( - self::CATEGORY_CUSTOM_RANKING, - ScopeInterface::SCOPE_STORE, - $storeId - )); - - $customRankings = $customRankings ?: []; - $customRankings = array_filter($customRankings, function ($customRanking) { - return $customRanking['attribute'] !== 'custom_attribute'; - }); - $attributes = $this->addIndexableAttributes($attributes, $customRankings, '0', '0'); - - if (is_array($attributes)) { - return $attributes; - } - - return []; + return $this->configInterface->getValue(self::API_KEY, ScopeInterface::SCOPE_STORE, $storeId); } - public function getProductAdditionalAttributes($storeId = null) + /** + * @param $storeId + * @return mixed + */ + public function getSearchOnlyAPIKey($storeId = null) { - $attributes = $this->unserialize($this->configInterface->getValue( - self::PRODUCT_ATTRIBUTES, - ScopeInterface::SCOPE_STORE, - $storeId - )); - - $facets = $this->unserialize($this->configInterface->getValue( - self::FACETS, - ScopeInterface::SCOPE_STORE, - $storeId - )); - $attributes = $this->addIndexableAttributes($attributes, $facets, '0'); - - $sorts = $this->unserialize($this->configInterface->getValue( - self::SORTING_INDICES, - ScopeInterface::SCOPE_STORE, - $storeId - )); - $attributes = $this->addIndexableAttributes($attributes, $sorts, '0'); - - $customRankings = $this->unserialize($this->configInterface->getValue( - self::PRODUCT_CUSTOM_RANKING, - ScopeInterface::SCOPE_STORE, - $storeId - )); - - $customRankings = $customRankings ?: []; - $customRankings = array_filter($customRankings, function ($customRanking) { - return $customRanking['attribute'] !== 'custom_attribute'; - }); - $attributes = $this->addIndexableAttributes($attributes, $customRankings, '0', '0'); - - if (is_array($attributes)) { - return $attributes; - } + return $this->configInterface->getValue(self::SEARCH_ONLY_API_KEY, ScopeInterface::SCOPE_STORE, $storeId); + } - return []; + /** + * @param $storeId + * @return mixed + */ + public function getIndexPrefix($storeId = null) + { + return $this->configInterface->getValue(self::INDEX_PREFIX, ScopeInterface::SCOPE_STORE, $storeId); } + /** + * @param $storeId + * @return array|bool|float|int|mixed|string + */ public function getFacets($storeId = null) { $attrs = $this->unserialize($this->configInterface->getValue( @@ -904,22 +1078,23 @@ public function getFacets($storeId = null) ScopeInterface::SCOPE_STORE, $storeId )); - if ($attrs) { foreach ($attrs as &$attr) { if ($attr['type'] === 'other') { $attr['type'] = $attr['other_type']; } } - if (is_array($attrs)) { return array_values($attrs); } } - return []; } + /** + * @param $storeId + * @return array + */ public function getCategoryCustomRanking($storeId = null) { $attrs = $this->unserialize($this->configInterface->getValue( @@ -927,17 +1102,27 @@ public function getCategoryCustomRanking($storeId = null) ScopeInterface::SCOPE_STORE, $storeId )); - if (is_array($attrs)) { return $attrs; } + return []; + } + /** + * @param $storeId + * @return array + */ + public function getProductCustomRanking($storeId = null) + { + $attrs = $this->unserialize($this->getRawProductCustomRanking($storeId)); + if (is_array($attrs)) { + return $attrs; + } return []; } /** - * @param int|null $storeId - * + * @param $storeId * @return mixed */ public function getRawProductCustomRanking($storeId = null) @@ -950,30 +1135,21 @@ public function getRawProductCustomRanking($storeId = null) } /** - * @param int|null $storeId - * - * @return array|mixed + * @param $section + * @param $storeId + * @return string */ - public function getProductCustomRanking($storeId = null) - { - $attrs = $this->unserialize($this->getRawProductCustomRanking($storeId)); - - if (is_array($attrs)) { - return $attrs; - } - - return []; - } - public function getExtraSettings($section, $storeId = null) { $constant = 'EXTRA_SETTINGS_' . mb_strtoupper($section); - $value = $this->configInterface->getValue(constant('self::' . $constant), ScopeInterface::SCOPE_STORE, $storeId); - - return trim((string) $value); + return trim((string)$value); } + /** + * @param $storeId + * @return bool + */ public function preventBackendRendering($storeId = null) { $preventBackendRendering = $this->configInterface->isSetFlag( @@ -981,42 +1157,37 @@ public function preventBackendRendering($storeId = null) ScopeInterface::SCOPE_STORE, $storeId ); - if ($preventBackendRendering === false) { return false; } - if (!isset($_SERVER['HTTP_USER_AGENT'])) { return false; } - $userAgent = mb_strtolower($_SERVER['HTTP_USER_AGENT'], 'utf-8'); - $allowedUserAgents = $this->configInterface->getValue( self::BACKEND_RENDERING_ALLOWED_USER_AGENTS, ScopeInterface::SCOPE_STORE, $storeId ); - $allowedUserAgents = trim($allowedUserAgents); - if ($allowedUserAgents === '') { return true; } - $allowedUserAgents = preg_split('/\n|\r\n?/', $allowedUserAgents); $allowedUserAgents = array_filter($allowedUserAgents); - foreach ($allowedUserAgents as $allowedUserAgent) { $allowedUserAgent = mb_strtolower($allowedUserAgent, 'utf-8'); if (mb_strpos($userAgent, $allowedUserAgent) !== false) { return false; } } - return true; } + /** + * @param $storeId + * @return mixed + */ public function getBackendRenderingDisplayMode($storeId = null) { return $this->configInterface->getValue( @@ -1026,11 +1197,19 @@ public function getBackendRenderingDisplayMode($storeId = null) ); } + /** + * @return int + * @throws Magento\Framework\Exception\NoSuchEntityException + */ public function getStoreId() { return $this->storeManager->getStore()->getId(); } + /** + * @param $storeId + * @return mixed + */ public function getStoreLocale($storeId) { return $this->configInterface->getValue( @@ -1040,61 +1219,51 @@ public function getStoreLocale($storeId) ); } + /** + * @param $storeId + * @return string|null + * @throws Magento\Framework\Exception\NoSuchEntityException + */ public function getCurrency($storeId = null) { /** @var Magento\Store\Model\Store $store */ $store = $this->storeManager->getStore($storeId); - $currencySymbol = $this->currency->getCurrency($store->getCurrentCurrencyCode())->getSymbol(); - - return $currencySymbol; - } - - public function getCurrencyCode($storeId = null) - { - /** @var Magento\Store\Model\Store $store */ - $store = $this->storeManager->getStore($storeId); - $code = $store->getCurrentCurrencyCode(); - - return $code; + return $this->currency->getCurrency($store->getCurrentCurrencyCode())->getSymbol(); } - public function getPopularQueries($storeId = null) + /** + * @param $storeId + * @return bool + */ + public function showSuggestionsOnNoResultsPage($storeId = null) { - if (!$this->isInstantEnabled($storeId) || !$this->showSuggestionsOnNoResultsPage($storeId)) { - return []; - } - - if ($storeId === null) { - $storeId = $this->storeManager->getStore()->getId(); - } - - /** @var SuggestionHelper $suggestionHelper */ - $suggestionHelper = $this->objectManager->create('Algolia\AlgoliaSearch\Helper\Entity\SuggestionHelper'); - $popularQueries = $suggestionHelper->getPopularQueries($storeId); - - return $popularQueries; + return $this->configInterface->isSetFlag( + self::SHOW_SUGGESTIONS_NO_RESULTS, + ScopeInterface::SCOPE_STORE, + $storeId + ); } + /** + * @param $groupId + * @return array + */ public function getAttributesToRetrieve($groupId) { if (false === $this->isCustomerGroupsEnabled()) { return []; } - $attributes = []; - foreach ($this->getProductAdditionalAttributes() as $attribute) { if ($attribute['attribute'] !== 'price' && $attribute['retrievable'] === '1') { $attributes[] = $attribute['attribute']; } } - foreach ($this->getCategoryAdditionalAttributes() as $attribute) { if ($attribute['retrievable'] === '1') { $attributes[] = $attribute['attribute']; } } - $attributes = array_merge($attributes, [ 'objectID', 'name', @@ -1112,9 +1281,7 @@ public function getAttributesToRetrieve($groupId) 'query', # suggestions 'path', # categories ]); - $currencies = $this->dirCurrency->getConfigAllowCurrencies(); - foreach ($currencies as $currency) { $attributes[] = 'price.' . $currency . '.default'; $attributes[] = 'price.' . $currency . '.default_tier'; @@ -1131,17 +1298,118 @@ public function getAttributesToRetrieve($groupId) $attributes[] = 'price.' . $currency . '.special_from_date'; $attributes[] = 'price.' . $currency . '.special_to_date'; } - $transport = new DataObject($attributes); $this->eventManager->dispatch('algolia_get_retrievable_attributes', ['attributes' => $transport]); $attributes = $transport->getData(); - $attributes = array_unique($attributes); $attributes = array_values($attributes); - return ['attributesToRetrieve' => $attributes]; } + /** + * @param $storeId + * @return array + */ + public function getProductAdditionalAttributes($storeId = null) + { + $attributes = $this->unserialize($this->configInterface->getValue( + self::PRODUCT_ATTRIBUTES, + ScopeInterface::SCOPE_STORE, + $storeId + )); + + $facets = $this->unserialize($this->configInterface->getValue( + self::FACETS, + ScopeInterface::SCOPE_STORE, + $storeId + )); + $attributes = $this->addIndexableAttributes($attributes, $facets, '0'); + + $sorts = $this->unserialize($this->configInterface->getValue( + self::SORTING_INDICES, + ScopeInterface::SCOPE_STORE, + $storeId + )); + $attributes = $this->addIndexableAttributes($attributes, $sorts, '0'); + + $customRankings = $this->unserialize($this->configInterface->getValue( + self::PRODUCT_CUSTOM_RANKING, + ScopeInterface::SCOPE_STORE, + $storeId + )); + $customRankings = $customRankings ?: []; + $customRankings = array_filter($customRankings, function ($customRanking) { + return $customRanking['attribute'] !== 'custom_attribute'; + }); + $attributes = $this->addIndexableAttributes($attributes, $customRankings, '0', '0'); + if (is_array($attributes)) { + return $attributes; + } + return []; + } + + /** + * @param $attributes + * @param $addedAttributes + * @param $searchable + * @param $retrievable + * @param $indexNoValue + * @return mixed + */ + protected function addIndexableAttributes( + $attributes, + $addedAttributes, + $searchable = '1', + $retrievable = '1', + $indexNoValue = '1' + ) { + foreach ((array)$addedAttributes as $addedAttribute) { + foreach ((array)$attributes as $attribute) { + if ($addedAttribute['attribute'] === $attribute['attribute']) { + continue 2; + } + } + $attributes[] = [ + 'attribute' => $addedAttribute['attribute'], + 'searchable' => $searchable, + 'retrievable' => $retrievable, + 'index_no_value' => $indexNoValue, + ]; + } + return $attributes; + } + + /** + * @param $storeId + * @return array + */ + public function getCategoryAdditionalAttributes($storeId = null) + { + $attributes = $this->unserialize($this->configInterface->getValue( + self::CATEGORY_ATTRIBUTES, + ScopeInterface::SCOPE_STORE, + $storeId + )); + $customRankings = $this->unserialize($this->configInterface->getValue( + self::CATEGORY_CUSTOM_RANKING, + ScopeInterface::SCOPE_STORE, + $storeId + )); + $customRankings = $customRankings ?: []; + $customRankings = array_filter($customRankings, function ($customRanking) { + return $customRanking['attribute'] !== 'custom_attribute'; + }); + $attributes = $this->addIndexableAttributes($attributes, $customRankings, '0', '0'); + if (is_array($attributes)) { + return $attributes; + } + return []; + } + + /** + * @param $groupId + * @return array + */ public function getAttributesToFilter($groupId) { $transport = new DataObject(); @@ -1150,18 +1418,24 @@ public function getAttributesToFilter($groupId) ['filter_object' => $transport, 'customer_group_id' => $groupId] ); $attributes = $transport->getData(); - $attributes = array_unique($attributes); $attributes = array_values($attributes); - return count($attributes) ? ['filters' => implode(' AND ', $attributes)] : []; } + /** + * @param $storeId + * @return bool + */ public function isEnabledSynonyms($storeId = null) { return $this->configInterface->isSetFlag(self::ENABLE_SYNONYMS, ScopeInterface::SCOPE_STORE, $storeId); } + /** + * @param $storeId + * @return array + */ public function getSynonyms($storeId = null) { $synonyms = $this->unserialize($this->configInterface->getValue( @@ -1169,14 +1443,16 @@ public function getSynonyms($storeId = null) ScopeInterface::SCOPE_STORE, $storeId )); - if (is_array($synonyms)) { return $synonyms; } - return []; } + /** + * @param $storeId + * @return array + */ public function getOnewaySynonyms($storeId = null) { $onewaySynonyms = $this->unserialize($this->configInterface->getValue( @@ -1184,37 +1460,49 @@ public function getOnewaySynonyms($storeId = null) ScopeInterface::SCOPE_STORE, $storeId )); - if (is_array($onewaySynonyms)) { return $onewaySynonyms; } - return []; } + /** + * @param $storeId + * @return string|null + * @throws Magento\Framework\Exception\FileSystemException + */ public function getSynonymsFile($storeId = null) { $filename = $this->configInterface->getValue(self::SYNONYMS_FILE, ScopeInterface::SCOPE_STORE, $storeId); - if (!$filename) { return null; } - $baseDirectory = $this->directoryList->getPath(DirectoryList::MEDIA); - return $baseDirectory . '/algoliasearch_admin_config_uploads/' . $filename; } + /** + * @param $storeId + * @return bool + */ public function isClickConversionAnalyticsEnabled($storeId = null) { return $this->configInterface->isSetFlag(self::CC_ANALYTICS_ENABLE, ScopeInterface::SCOPE_STORE, $storeId); } + /** + * @param $storeId + * @return mixed + */ public function getClickConversionAnalyticsISSelector($storeId = null) { return $this->configInterface->getValue(self::CC_ANALYTICS_IS_SELECTOR, ScopeInterface::SCOPE_STORE, $storeId); } + /** + * @param $storeId + * @return mixed + */ public function getConversionAnalyticsMode($storeId = null) { return $this->configInterface->getValue( @@ -1224,16 +1512,19 @@ public function getConversionAnalyticsMode($storeId = null) ); } + /** + * @param $storeId + * @return mixed + */ public function getConversionAnalyticsAddToCartSelector($storeId = null) { return $this->configInterface->getValue(self::CC_ADD_TO_CART_SELECTOR, ScopeInterface::SCOPE_STORE, $storeId); } - public function isAnalyticsEnabled($storeId = null) - { - return $this->configInterface->isSetFlag(self::GA_ENABLE, ScopeInterface::SCOPE_STORE, $storeId); - } - + /** + * @param $storeId + * @return array + */ public function getAnalyticsConfig($storeId = null) { return [ @@ -1252,16 +1543,27 @@ public function getAnalyticsConfig($storeId = null) ]; } + /** + * @param $storeId + * @return bool + */ + public function isAnalyticsEnabled($storeId = null) + { + return $this->configInterface->isSetFlag(self::GA_ENABLE, ScopeInterface::SCOPE_STORE, $storeId); + } + + /** + * @param $storeId + * @return array + */ public function getNonCastableAttributes($storeId = null) { $nonCastableAttributes = []; - $config = $this->unserialize($this->configInterface->getValue( self::NON_CASTABLE_ATTRIBUTES, ScopeInterface::SCOPE_STORE, $storeId )); - if (is_array($config)) { foreach ($config as $attributeData) { if (isset($attributeData['attribute'])) { @@ -1269,66 +1571,43 @@ public function getNonCastableAttributes($storeId = null) } } } - return $nonCastableAttributes; } - private function addIndexableAttributes( - $attributes, - $addedAttributes, - $searchable = '1', - $retrievable = '1', - $indexNoValue = '1' - ) { - foreach ((array) $addedAttributes as $addedAttribute) { - foreach ((array) $attributes as $attribute) { - if ($addedAttribute['attribute'] === $attribute['attribute']) { - continue 2; - } - } - - $attributes[] = [ - 'attribute' => $addedAttribute['attribute'], - 'searchable' => $searchable, - 'retrievable' => $retrievable, - 'index_no_value' => $indexNoValue, - ]; - } - - return $attributes; - } - - private function unserialize($value) - { - if (false === $value || null === $value || '' === $value) { - return false; - } - - $unserialized = json_decode($value, true); - - if (json_last_error() === JSON_ERROR_NONE) { - return $unserialized; - } - - return $this->serializer->unserialize($value); - } - - public function getDefaultMaxRecordSize() - { - return self::DEFAULT_MAX_RECORD_SIZE; - } - + /** + * @param $storeId + * @return int + */ public function getMaxRecordSizeLimit($storeId = null) { - return self::getDefaultMaxRecordSize(); + return (int)$this->configInterface->getValue( + self::MAX_RECORD_SIZE_LIMIT, + ScopeInterface::SCOPE_STORE, + $storeId + ); } + /** + * @param $storeId + * @return int + */ public function getArchiveLogClearLimit($storeId = null) { - return (int) $this->configInterface->getValue( + return (int)$this->configInterface->getValue( self::ARCHIVE_LOG_CLEAR_LIMIT, ScopeInterface::SCOPE_STORE, $storeId ); } + + /** + * @param $storeId + * @return mixed + */ + public function getCacheTime($storeId = null) { + return $this->configInterface->getValue(self::MAGENTO_DEFAULT_CACHE_TIME, + ScopeInterface::SCOPE_STORE, + $storeId + ); + } } diff --git a/Helper/Entity/CategoryHelper.php b/Helper/Entity/CategoryHelper.php index 9a7845a65..a31209153 100755 --- a/Helper/Entity/CategoryHelper.php +++ b/Helper/Entity/CategoryHelper.php @@ -22,36 +22,42 @@ class CategoryHelper { - private $eventManager; + /** @var ManagerInterface */ + protected $eventManager; - private $storeManager; + /** @var StoreManagerInterface */ + protected $storeManager; - private $resourceConnection; + /** @var ResourceConnection */ + protected $resourceConnection; - private $eavConfig; + /** @var Config */ + protected $eavConfig; - private $configHelper; + /** @var ConfigHelper */ + protected $configHelper; /** @var CategoryCollectionFactory */ - private $categoryCollectionFactory; + protected $categoryCollectionFactory; /** @var Image */ - private $imageHelper; + protected $imageHelper; /** @var CategoryResource */ - private $categoryResource; + protected $categoryResource; /** @var CategoryRepository */ - private $categoryRepository; + protected $categoryRepository; - private $isCategoryVisibleInMenuCache; - private $coreCategories; - private $idColumn; - private $categoryAttributes; - private $rootCategoryId = -1; - private $activeCategories; - private $categoryNames; - private $moduleManager; + protected $isCategoryVisibleInMenuCache; + protected $idColumn; + protected $categoryAttributes; + protected $rootCategoryId = -1; + protected $activeCategories; + protected $categoryNames; + + /** @var Manager*/ + protected $moduleManager; /** * CategoryHelper constructor. @@ -90,11 +96,18 @@ public function __construct( $this->moduleManager = $moduleManager; } + /** + * @return string + */ public function getIndexNameSuffix() { return '_categories'; } + /** + * @param $storeId + * @return array|mixed|null + */ public function getIndexSettings($storeId) { $searchableAttributes = []; @@ -144,6 +157,10 @@ public function getIndexSettings($storeId) return $indexSettings; } + /** + * @param $storeId + * @return array + */ public function getAdditionalAttributes($storeId = null) { return $this->configHelper->getCategoryAdditionalAttributes($storeId); @@ -211,6 +228,10 @@ public function canCategoryBeReindexed($category, $storeId) return true; } + /** + * @return array + * @throws \Magento\Framework\Exception\LocalizedException + */ public function getAllAttributes() { if (isset($this->categoryAttributes)) { @@ -243,6 +264,11 @@ public function getAllAttributes() return $this->categoryAttributes; } + /** + * @param MagentoCategory $category + * @return array|mixed|null + * @throws \Magento\Framework\Exception\LocalizedException + */ public function getObject(Category $category) { /** @var \Magento\Catalog\Model\ResourceModel\Product\Collection $productCollection */ @@ -327,6 +353,10 @@ public function getObject(Category $category) return $data; } + /** + * @return int|mixed + * @throws \Magento\Framework\Exception\LocalizedException + */ public function getRootCategoryId() { if ($this->rootCategoryId !== -1) { @@ -343,6 +373,10 @@ public function getRootCategoryId() return $this->rootCategoryId; } + /** + * @param MagentoCategory $category + * @return array|string|string[] + */ private function getUrl(Category $category) { $categoryUrl = $category->getUrl(); @@ -403,7 +437,6 @@ public function getCategoryName($categoryId, $storeId = null) $categoryId = (int) $categoryId; $storeId = (int) $storeId; - if (!isset($this->categoryNames)) { $this->categoryNames = []; @@ -434,7 +467,7 @@ public function getCategoryName($categoryId, $storeId = null) $categoryName = null; - $categoryKeyId = $this->getCategoryKeyId($categoryId); + $categoryKeyId = $this->getCategoryKeyId($categoryId, $storeId); if ($categoryKeyId === null) { return $categoryName; @@ -456,26 +489,41 @@ public function getCategoryName($categoryId, $storeId = null) return $categoryName; } - private function getCategoryKeyId($categoryId) + /** + * @param $categoryId + * @param $storeId + * @return mixed|null + */ + private function getCategoryKeyId($categoryId, $storeId = null) { $categoryKeyId = $categoryId; if ($this->getCorrectIdColumn() === 'row_id') { - $category = $this->getCategoryById($categoryId); - + $category = $this->getCategoryById($categoryId, $storeId); return $category ? $category->getRowId() : null; } return $categoryKeyId; } - private function getCategoryById($categoryId) + /** + * @param $categoryId + * @param $storeId + * @return mixed|null + */ + private function getCategoryById($categoryId, $storeId = null) { - $categories = $this->getCoreCategories(false); + $categories = $this->getCoreCategories(false, $storeId); return isset($categories[$categoryId]) ? $categories[$categoryId] : null; } + /** + * @param $categoryId + * @param $storeId + * @return bool|mixed + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ public function isCategoryVisibleInMenu($categoryId, $storeId) { $key = $categoryId . '-' . $storeId; @@ -492,16 +540,19 @@ public function isCategoryVisibleInMenu($categoryId, $storeId) return $this->isCategoryVisibleInMenuCache[$key]; } - public function getCoreCategories($filterNotIncludedCategories = true) + /** + * @param $filterNotIncludedCategories + * @param $storeId + * @return array + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function getCoreCategories($filterNotIncludedCategories = true, $storeId = null) { $key = $filterNotIncludedCategories ? 'filtered' : 'non_filtered'; - if (isset($this->coreCategories[$key])) { - return $this->coreCategories[$key]; - } - $collection = $this->categoryCollectionFactory->create() ->distinct(true) + ->setStoreId($storeId) ->addNameToResult() ->addIsActiveFilter() ->addAttributeToSelect('name') @@ -511,16 +562,19 @@ public function getCoreCategories($filterNotIncludedCategories = true) $collection->addAttributeToFilter('include_in_menu', '1'); } - $this->coreCategories[$key] = []; + $coreCategories[$key] = []; /** @var \Magento\Catalog\Model\Category $category */ foreach ($collection as $category) { - $this->coreCategories[$key][$category->getId()] = $category; + $coreCategories[$key][$category->getId()] = $category; } - return $this->coreCategories[$key]; + return $coreCategories[$key]; } + /** + * @return string + */ private function getCorrectIdColumn() { if (isset($this->idColumn)) { diff --git a/Helper/Entity/ProductHelper.php b/Helper/Entity/ProductHelper.php index d2ad78776..276cd7a9a 100755 --- a/Helper/Entity/ProductHelper.php +++ b/Helper/Entity/ProductHelper.php @@ -35,35 +35,86 @@ class ProductHelper { + /** + * @var CollectionFactory + */ protected $productCollectionFactory; + /** + * @var GroupCollection + */ protected $groupCollection; - private $eavConfig; - private $configHelper; - private $algoliaHelper; - private $logger; - private $storeManager; - private $eventManager; - private $visibility; - private $stockHelper; - private $stockRegistry; - private $currencyManager; - private $categoryHelper; - private $priceManager; - private $imageHelper; + /** + * @var Config + */ + protected $eavConfig; + /** + * @var ConfigHelper + */ + protected $configHelper; + /** + * @var AlgoliaHelper + */ + protected $algoliaHelper; + /** + * @var Logger + */ + protected $logger; + /** + * @var StoreManagerInterface + */ + protected $storeManager; + /** + * @var ManagerInterface + */ + protected $eventManager; + /** + * @var Visibility + */ + protected $visibility; + /** + * @var Stock + */ + protected $stockHelper; + /** + * @var StockRegistryInterface + */ + protected $stockRegistry; + /** + * @var CurrencyHelper + */ + protected $currencyManager; + /** + * @var CategoryHelper + */ + protected $categoryHelper; + /** + * @var PriceManager + */ + protected $priceManager; + /** + * @var ImageHelper + */ + protected $imageHelper; /** * @var Type */ - private $productType; + protected $productType; /** * @var AbstractType[] */ - private $compositeTypes; + protected $compositeTypes; - private $productAttributes; + /** + * @var + */ + protected $productAttributes; - private $predefinedProductAttributes = [ + /** + * @var string[] + */ + protected $predefinedProductAttributes = [ 'name', 'url_key', 'image', @@ -72,7 +123,10 @@ class ProductHelper 'msrp_enabled', // Needed to handle MSRP behavior ]; - private $createdAttributes = [ + /** + * @var string[] + */ + protected $createdAttributes = [ 'path', 'categories', 'categories_without_path', @@ -84,7 +138,10 @@ class ProductHelper 'in_stock', ]; - private $attributesToIndexAsArray = [ + /** + * @var string[] + */ + protected $attributesToIndexAsArray = [ 'sku', 'color', ]; @@ -145,11 +202,19 @@ public function __construct( $this->imageHelper = $imageHelper; } + /** + * @return string + */ public function getIndexNameSuffix() { return '_products'; } + /** + * @param $addEmptyRow + * @return array + * @throws \Magento\Framework\Exception\LocalizedException + */ public function getAllAttributes($addEmptyRow = false) { if (!isset($this->productAttributes)) { @@ -202,6 +267,11 @@ public function getAllAttributes($addEmptyRow = false) return $attributes; } + /** + * @param $additionalAttributes + * @param $attributeName + * @return bool + */ public function isAttributeEnabled($additionalAttributes, $attributeName) { foreach ($additionalAttributes as $attr) { @@ -213,6 +283,13 @@ public function isAttributeEnabled($additionalAttributes, $attributeName) return false; } + /** + * @param $storeId + * @param $productIds + * @param $onlyVisible + * @param $includeNotVisibleIndividually + * @return \Magento\Catalog\Model\ResourceModel\Product\Collection + */ public function getProductCollectionQuery( $storeId, $productIds = null, @@ -272,6 +349,11 @@ public function getProductCollectionQuery( return $products; } + /** + * @param $products + * @param $storeId + * @return void + */ protected function addStockFilter($products, $storeId) { if ($this->configHelper->getShowOutOfStock($storeId) === false) { @@ -279,6 +361,10 @@ protected function addStockFilter($products, $storeId) } } + /** + * @param $products + * @return void + */ protected function addMandatoryAttributes($products) { /** @var \Magento\Catalog\Model\ResourceModel\Product\Collection $products */ @@ -290,11 +376,23 @@ protected function addMandatoryAttributes($products) ->addAttributeToSelect('status'); } + /** + * @param $storeId + * @return array + */ public function getAdditionalAttributes($storeId = null) { return $this->configHelper->getProductAdditionalAttributes($storeId); } + /** + * @param $indexName + * @param $indexNameTmp + * @param $storeId + * @param $saveToTmpIndicesToo + * @return void + * @throws AlgoliaException + */ public function setSettings($indexName, $indexNameTmp, $storeId, $saveToTmpIndicesToo = false) { $searchableAttributes = $this->getSearchableAttributes($storeId); @@ -445,10 +543,16 @@ public function setSettings($indexName, $indexNameTmp, $storeId, $saveToTmpIndic } } + /** + * @param $categoryIds + * @param $storeId + * @return array + * @throws \Magento\Framework\Exception\LocalizedException + */ public function getAllCategories($categoryIds, $storeId) { - $filterNotIncludedCategories = !$this->configHelper->showCatsNotIncludedInNavigation($storeId); - $categories = $this->categoryHelper->getCoreCategories($filterNotIncludedCategories); + $filterNotIncludedCategories = $this->configHelper->showCatsNotIncludedInNavigation($storeId); + $categories = $this->categoryHelper->getCoreCategories($filterNotIncludedCategories, $storeId); $selectedCategories = []; foreach ($categoryIds as $id) { @@ -460,6 +564,11 @@ public function getAllCategories($categoryIds, $storeId) return $selectedCategories; } + /** + * @param Product $product + * @return array|mixed|null + * @throws \Exception + */ public function getObject(Product $product) { $storeId = $product->getStoreId(); @@ -501,30 +610,21 @@ public function getObject(Product $product) $customData = $this->addAttribute('ordered_qty', $defaultData, $customData, $additionalAttributes, $product); $customData = $this->addAttribute('total_ordered', $defaultData, $customData, $additionalAttributes, $product); $customData = $this->addAttribute('rating_summary', $defaultData, $customData, $additionalAttributes, $product); - $customData = $this->addCategoryData($customData, $product); $customData = $this->addImageData($customData, $product, $additionalAttributes); - $customData = $this->addInStock($defaultData, $customData, $product); $customData = $this->addStockQty($defaultData, $customData, $additionalAttributes, $product); - $subProducts = $this->getSubProducts($product); - $customData = $this->addAdditionalAttributes($customData, $additionalAttributes, $product, $subProducts); - $customData = $this->priceManager->addPriceDataByProductType($customData, $product, $subProducts); - $transport = new DataObject($customData); $this->eventManager->dispatch( 'algolia_subproducts_index', ['custom_data' => $transport, 'sub_products' => $subProducts, 'productObject' => $product] ); $customData = $transport->getData(); - $customData = array_merge($customData, $defaultData); - $this->algoliaHelper->castProductObject($customData); - $transport = new DataObject($customData); $this->eventManager->dispatch( 'algolia_after_create_product_object', @@ -537,7 +637,11 @@ public function getObject(Product $product) return $customData; } - private function getSubProducts(Product $product) + /** + * @param Product $product + * @return array|\Magento\Catalog\Api\Data\ProductInterface[]|DataObject[] + */ + protected function getSubProducts(Product $product) { $type = $product->getTypeId(); @@ -595,7 +699,7 @@ public function getParentProductIds(array $productIds) * * @see \Magento\Catalog\Model\Indexer\Product\Flat\AbstractAction::_getProductTypeInstances */ - private function getCompositeTypes() + protected function getCompositeTypes() { if ($this->compositeTypes === null) { $productEmulator = new \Magento\Framework\DataObject(); @@ -608,7 +712,15 @@ private function getCompositeTypes() return $this->compositeTypes; } - private function addAttribute($attribute, $defaultData, $customData, $additionalAttributes, Product $product) + /** + * @param $attribute + * @param $defaultData + * @param $customData + * @param $additionalAttributes + * @param Product $product + * @return mixed + */ + protected function addAttribute($attribute, $defaultData, $customData, $additionalAttributes, Product $product) { if (isset($defaultData[$attribute]) === false && $this->isAttributeEnabled($additionalAttributes, $attribute)) { @@ -618,10 +730,16 @@ private function addAttribute($attribute, $defaultData, $customData, $additional return $customData; } - private function addCategoryData($customData, Product $product) + /** + * @param $customData + * @param Product $product + * @return mixed + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + protected function addCategoryData($customData, Product $product) { $storeId = $product->getStoreId(); - $categories = []; $categoriesWithPath = []; $categoryIds = []; @@ -645,7 +763,6 @@ private function addCategoryData($customData, Product $product) } $categoryName = $this->categoryHelper->getCategoryName($category->getId(), $storeId); - if ($categoryName) { $categories[] = $categoryName; } @@ -685,7 +802,11 @@ private function addCategoryData($customData, Product $product) return $customData; } - private function getHierarchicalCategories($categoriesWithPath) + /** + * @param $categoriesWithPath + * @return array + */ + protected function getHierarchicalCategories($categoriesWithPath) { $hierachivalCategories = []; @@ -713,7 +834,13 @@ private function getHierarchicalCategories($categoriesWithPath) return $hierachivalCategories; } - private function addImageData(array $customData, Product $product, $additionalAttributes) + /** + * @param array $customData + * @param Product $product + * @param $additionalAttributes + * @return array + */ + protected function addImageData(array $customData, Product $product, $additionalAttributes) { if (false === isset($customData['thumbnail_url'])) { $customData['thumbnail_url'] = $this->imageHelper @@ -745,6 +872,12 @@ private function addImageData(array $customData, Product $product, $additionalAt return $customData; } + /** + * @param $defaultData + * @param $customData + * @param Product $product + * @return mixed + */ protected function addInStock($defaultData, $customData, Product $product) { if (isset($defaultData['in_stock']) === false) { @@ -755,7 +888,14 @@ protected function addInStock($defaultData, $customData, Product $product) return $customData; } - private function addStockQty($defaultData, $customData, $additionalAttributes, Product $product) + /** + * @param $defaultData + * @param $customData + * @param $additionalAttributes + * @param Product $product + * @return mixed + */ + protected function addStockQty($defaultData, $customData, $additionalAttributes, Product $product) { if (isset($defaultData['stock_qty']) === false && $this->isAttributeEnabled($additionalAttributes, 'stock_qty')) { @@ -770,7 +910,15 @@ private function addStockQty($defaultData, $customData, $additionalAttributes, P return $customData; } - private function addAdditionalAttributes($customData, $additionalAttributes, Product $product, $subProducts) + /** + * @param $customData + * @param $additionalAttributes + * @param Product $product + * @param $subProducts + * @return mixed + * @throws \Magento\Framework\Exception\LocalizedException + */ + protected function addAdditionalAttributes($customData, $additionalAttributes, Product $product, $subProducts) { foreach ($additionalAttributes as $attribute) { $attributeName = $attribute['attribute']; @@ -811,7 +959,14 @@ private function addAdditionalAttributes($customData, $additionalAttributes, Pro return $customData; } - private function addNullValue($customData, $subProducts, $attribute, AttributeResource $attributeResource) + /** + * @param $customData + * @param $subProducts + * @param $attribute + * @param AttributeResource $attributeResource + * @return mixed + */ + protected function addNullValue($customData, $subProducts, $attribute, AttributeResource $attributeResource) { $attributeName = $attribute['attribute']; @@ -852,7 +1007,13 @@ private function addNullValue($customData, $subProducts, $attribute, AttributeRe return $customData; } - private function getValues($valueText, Product $subProduct, AttributeResource $attributeResource) + /** + * @param $valueText + * @param Product $subProduct + * @param AttributeResource $attributeResource + * @return array + */ + protected function getValues($valueText, Product $subProduct, AttributeResource $attributeResource) { $values = []; @@ -871,7 +1032,14 @@ private function getValues($valueText, Product $subProduct, AttributeResource $a return $values; } - private function addSubProductImage($subProductImages, $attribute, $subProduct, $valueText) + /** + * @param $subProductImages + * @param $attribute + * @param $subProduct + * @param $valueText + * @return mixed + */ + protected function addSubProductImage($subProductImages, $attribute, $subProduct, $valueText) { if (mb_strtolower($attribute['attribute'], 'utf-8') !== 'color') { return $subProductImages; @@ -900,7 +1068,15 @@ private function addSubProductImage($subProductImages, $attribute, $subProduct, return $subProductImages; } - private function addNonNullValue( + /** + * @param $customData + * @param $value + * @param Product $product + * @param $attribute + * @param AttributeResource $attributeResource + * @return mixed + */ + protected function addNonNullValue( $customData, $value, Product $product, @@ -927,7 +1103,11 @@ private function addNonNullValue( return $customData; } - private function getSearchableAttributes($storeId = null) + /** + * @param $storeId + * @return array + */ + protected function getSearchableAttributes($storeId = null) { $searchableAttributes = []; @@ -951,7 +1131,11 @@ private function getSearchableAttributes($storeId = null) return $searchableAttributes; } - private function getCustomRanking($storeId) + /** + * @param $storeId + * @return array + */ + protected function getCustomRanking($storeId) { $customRanking = []; @@ -963,7 +1147,11 @@ private function getCustomRanking($storeId) return $customRanking; } - private function getUnretrieveableAttributes($storeId = null) + /** + * @param $storeId + * @return array + */ + protected function getUnretrieveableAttributes($storeId = null) { $unretrievableAttributes = []; @@ -976,7 +1164,11 @@ private function getUnretrieveableAttributes($storeId = null) return $unretrievableAttributes; } - private function getAttributesForFaceting($storeId) + /** + * @param $storeId + * @return array + */ + protected function getAttributesForFaceting($storeId) { $attributesForFaceting = []; @@ -1022,7 +1214,13 @@ private function getAttributesForFaceting($storeId) return $attributesForFaceting; } - private function deleteUnusedReplicas($indexName, $replicas, $setReplicasTaskId) + /** + * @param $indexName + * @param $replicas + * @param $setReplicasTaskId + * @return void + */ + protected function deleteUnusedReplicas($indexName, $replicas, $setReplicasTaskId) { $indicesToDelete = []; @@ -1046,7 +1244,12 @@ private function deleteUnusedReplicas($indexName, $replicas, $setReplicasTaskId) } } - private function setFacetsQueryRules($indexName) + /** + * @param $indexName + * @return void + * @throws AlgoliaException + */ + protected function setFacetsQueryRules($indexName) { $index = $this->algoliaHelper->getIndex($indexName); @@ -1090,7 +1293,12 @@ private function setFacetsQueryRules($indexName) } } - private function clearFacetsQueryRules(SearchIndex $index) + /** + * @param SearchIndex $index + * @return void + * @throws AlgoliaException + */ + protected function clearFacetsQueryRules(SearchIndex $index) { try { $hitsPerPage = 100; @@ -1123,7 +1331,11 @@ private function clearFacetsQueryRules(SearchIndex $index) } } - private function explodeSynonyms($synonyms) + /** + * @param $synonyms + * @return array + */ + protected function explodeSynonyms($synonyms) { return array_map('trim', explode(',', $synonyms)); } diff --git a/Helper/Entity/SuggestionHelper.php b/Helper/Entity/SuggestionHelper.php index 581d6510b..24d33526b 100755 --- a/Helper/Entity/SuggestionHelper.php +++ b/Helper/Entity/SuggestionHelper.php @@ -8,24 +8,38 @@ use Magento\Framework\Event\ManagerInterface; use Magento\Framework\Serialize\SerializerInterface; use Magento\Search\Model\Query; +use Magento\Search\Model\ResourceModel\Query\Collection as QueryCollection; use Magento\Search\Model\ResourceModel\Query\CollectionFactory as QueryCollectionFactory; class SuggestionHelper { + /*** + * @var ManagerInterface + */ private $eventManager; - /** * @var QueryCollectionFactory */ - private $queryCollectionFactory; - + protected $queryCollectionFactory; + /** + * @var ConfigCache + */ private $cache; + /** + * @var ConfigHelper + */ private $configHelper; + /** + * @var SerializerInterface + */ private $serializer; - private $popularQueriesCacheId = 'algoliasearch_popular_queries_cache_tag'; + /** + * @var string + */ + public const POPULAR_QUERIES_CACHE_TAG = 'algoliasearch_popular_queries_cache_tag'; /** * SuggestionHelper constructor. @@ -50,11 +64,18 @@ public function __construct( $this->serializer = $serializer; } + /** + * @return string + */ public function getIndexNameSuffix() { return '_suggestions'; } + /** + * @param $storeId + * @return array|mixed|null + */ public function getIndexSettings($storeId) { $indexSettings = [ @@ -69,11 +90,13 @@ public function getIndexSettings($storeId) 'algolia_suggestions_index_before_set_settings', ['store_id' => $storeId, 'index_settings' => $transport] ); - $indexSettings = $transport->getData(); - - return $indexSettings; + return $transport->getData(); } + /** + * @param Query $suggestion + * @return array|mixed|null + */ public function getObject(Query $suggestion) { $suggestionObject = [ @@ -89,23 +112,28 @@ public function getObject(Query $suggestion) 'algolia_after_create_suggestion_object', ['suggestion' => $transport, 'suggestionObject' => $suggestion] ); - $suggestionObject = $transport->getData(); - - return $suggestionObject; + return $transport->getData(); } - public function getPopularQueries($storeId) + /** + * @param $storeId + * @return array|bool|float|int|string|null + */ + public function getPopularQueries($storeId = null) { - $queries = $this->cache->load($this->popularQueriesCacheId); + if (!$this->configHelper->isInstantEnabled($storeId) || !$this->configHelper->showSuggestionsOnNoResultsPage($storeId)) { + return []; + } + $queries = $this->cache->load(self::POPULAR_QUERIES_CACHE_TAG . '_' . $storeId); if ($queries !== false) { return $this->serializer->unserialize($queries); } - /** @var \Magento\Search\Model\ResourceModel\Query\Collection $collection */ + /** @var QueryCollection $collection */ $collection = $this->queryCollectionFactory->create(); $collection->getSelect()->where( - 'num_results >= ' . $this->configHelper->getMinNumberOfResults() . ' - AND popularity >= ' . $this->configHelper->getMinPopularity() . ' + 'num_results >= ' . $this->configHelper->getMinNumberOfResults() . ' + AND popularity >= ' . $this->configHelper->getMinPopularity() . ' AND query_text != "__empty__" AND CHAR_LENGTH(query_text) >= 3' ); @@ -121,21 +149,30 @@ public function getPopularQueries($storeId) $queries = $collection->getColumnValues('query_text'); - $this->cache->save($this->serializer->serialize($queries), $this->popularQueriesCacheId, [], 24*3600); + $this->cache->save( + $this->serializer->serialize($queries), + self::POPULAR_QUERIES_CACHE_TAG . '_' . $storeId, + [], + $this->configHelper->getCacheTime($storeId) + ); return $queries; } + /** + * @param $storeId + * @return QueryCollection + */ public function getSuggestionCollectionQuery($storeId) { - /** @var \Magento\Search\Model\ResourceModel\Query\Collection $collection */ + /** @var QueryCollection $collection */ $collection = $this->queryCollectionFactory->create() ->addStoreFilter($storeId) ->setStoreId($storeId); $collection->getSelect()->where( - 'num_results >= ' . $this->configHelper->getMinNumberOfResults($storeId) . ' - AND popularity >= ' . $this->configHelper->getMinPopularity($storeId) . ' + 'num_results >= ' . $this->configHelper->getMinNumberOfResults($storeId) . ' + AND popularity >= ' . $this->configHelper->getMinPopularity($storeId) . ' AND query_text != "__empty__"' ); @@ -146,4 +183,4 @@ public function getSuggestionCollectionQuery($storeId) return $collection; } -} +} \ No newline at end of file diff --git a/Model/Queue.php b/Model/Queue.php index a480ad960..1aec3a767 100644 --- a/Model/Queue.php +++ b/Model/Queue.php @@ -25,50 +25,58 @@ class Queue public const ERROR_LOG = 'algoliasearch_queue_errors.log'; /** @var AdapterInterface */ - private $db; + protected $db; /** @var string */ - private $table; + protected $table; /** @var string */ - private $logTable; + protected $logTable; /** @var string */ - private $archiveTable; + protected $archiveTable; /** @var ObjectManagerInterface */ - private $objectManager; + protected $objectManager; /** @var ConsoleOutput */ - private $output; + protected $output; /** @var int */ - private $elementsPerPage; + protected $elementsPerPage; /** @var ConfigHelper */ - private $configHelper; + protected $configHelper; /** @var Logger */ - private $logger; + protected $logger; - private $jobCollectionFactory; + protected $jobCollectionFactory; /** @var int */ - private $maxSingleJobDataSize; + protected $maxSingleJobDataSize; /** @var int */ - private $noOfFailedJobs = 0; + protected $noOfFailedJobs = 0; /** @var array */ - private $staticJobMethods = [ + protected $staticJobMethods = [ 'saveConfigurationToAlgolia', 'moveIndexWithSetSettings', 'deleteObjects', ]; /** @var array */ - private $logRecord; + protected $logRecord; + /** + * @param ConfigHelper $configHelper + * @param Logger $logger + * @param JobCollectionFactory $jobCollectionFactory + * @param ResourceConnection $resourceConnection + * @param ObjectManagerInterface $objectManager + * @param ConsoleOutput $output + */ public function __construct( ConfigHelper $configHelper, Logger $logger, @@ -117,6 +125,7 @@ public function addToQueue($className, $method, array $data, $dataSize = 1, $isF 'data' => json_encode($data), 'data_size' => $dataSize, 'pid' => null, + 'max_retries' => $this->configHelper->getRetryLimit(), 'is_full_reindex' => $isFullReindex ? 1 : 0, ]); } else { @@ -136,8 +145,8 @@ public function addToQueue($className, $method, array $data, $dataSize = 1, $isF public function getAverageProcessingTime() { $select = $this->db->select() - ->from($this->logTable, ['number_of_runs' => 'COUNT(duration)', 'average_time' => 'AVG(duration)']) - ->where('processed_jobs > 0 AND with_empty_queue = 0 AND started >= (CURDATE() - INTERVAL 2 DAY)'); + ->from($this->logTable, ['number_of_runs' => 'COUNT(duration)', 'average_time' => 'AVG(duration)']) + ->where('processed_jobs > 0 AND with_empty_queue = 0 AND started >= (CURDATE() - INTERVAL 2 DAY)'); $data = $this->db->query($select)->fetch(); @@ -264,11 +273,11 @@ public function run($maxJobs) /** * @param string $whereClause */ - private function archiveFailedJobs($whereClause) + protected function archiveFailedJobs($whereClause) { $select = $this->db->select() - ->from($this->table, ['pid', 'class', 'method', 'data', 'error_log', 'data_size', 'NOW()']) - ->where($whereClause); + ->from($this->table, ['pid', 'class', 'method', 'data', 'error_log', 'data_size', 'NOW()']) + ->where($whereClause); $query = $this->db->insertFromSelect( $select, @@ -287,7 +296,7 @@ private function archiveFailedJobs($whereClause) * @return Job[] * */ - private function getJobs($maxJobs) + protected function getJobs($maxJobs) { $maxJobs = ($maxJobs === -1) ? $this->configHelper->getNumberOfJobToRun() : $maxJobs; @@ -334,7 +343,7 @@ private function getJobs($maxJobs) * * @return Job[] */ - private function fetchJobs($jobsLimit, $fetchFullReindexJobs = false, $lastJobId = null) + protected function fetchJobs($jobsLimit, $fetchFullReindexJobs = false, $lastJobId = null) { $jobs = []; @@ -353,8 +362,8 @@ private function fetchJobs($jobsLimit, $fetchFullReindexJobs = false, $lastJobId ->addFieldToFilter('is_full_reindex', $fetchFullReindexJobs) ->setOrder('job_id', Collection::SORT_ORDER_ASC) ->getSelect() - ->limit($limit, $offset) - ->forUpdate(); + ->limit($limit, $offset) + ->forUpdate(); if ($lastJobId !== null) { $jobsCollection->addFieldToFilter('job_id', ['gt' => $lastJobId]); @@ -404,7 +413,7 @@ private function fetchJobs($jobsLimit, $fetchFullReindexJobs = false, $lastJobId * * @return Job[] */ - private function mergeJobs(array $unmergedJobs) + protected function mergeJobs(array $unmergedJobs) { $unmergedJobs = $this->sortJobs($unmergedJobs); @@ -441,7 +450,7 @@ private function mergeJobs(array $unmergedJobs) * * @return Job[] */ - private function sortJobs(array $jobs) + protected function sortJobs(array $jobs) { $sortedJobs = []; @@ -473,7 +482,7 @@ private function sortJobs(array $jobs) * * @return array */ - private function stackSortedJobs(array $sortedJobs, array $tempSortableJobs, Job $job = null) + protected function stackSortedJobs(array $sortedJobs, array $tempSortableJobs, Job $job = null) { if ($tempSortableJobs && $tempSortableJobs !== []) { $tempSortableJobs = $this->jobSort( @@ -501,7 +510,7 @@ private function stackSortedJobs(array $sortedJobs, array $tempSortableJobs, Job /** * @return array */ - private function jobSort() + protected function jobSort() { $args = func_get_args(); @@ -533,7 +542,7 @@ private function jobSort() /** * @param Job[] $jobs */ - private function lockJobs(array $jobs) + protected function lockJobs(array $jobs) { $jobsIds = $this->getJobsIdsFromMergedJobs($jobs); @@ -551,7 +560,7 @@ private function lockJobs(array $jobs) * * @return string[] */ - private function getJobsIdsFromMergedJobs(array $mergedJobs) + protected function getJobsIdsFromMergedJobs(array $mergedJobs) { $jobsIds = []; foreach ($mergedJobs as $job) { @@ -561,17 +570,11 @@ private function getJobsIdsFromMergedJobs(array $mergedJobs) return $jobsIds; } - private function clearOldFailingJobs() + /** + * @return void + */ + protected function clearOldFailingJobs() { - $retryLimit = $this->configHelper->getRetryLimit(); - - if ($retryLimit > 0) { - $where = $this->db->quoteInto('retries >= ?', $retryLimit); - $this->archiveFailedJobs($where); - - return; - } - $this->archiveFailedJobs('retries > max_retries'); $this->db->delete($this->table, 'retries > max_retries'); } @@ -579,12 +582,12 @@ private function clearOldFailingJobs() /** * @throws Zend_Db_Statement_Exception */ - private function clearOldLogRecords() + protected function clearOldLogRecords() { $select = $this->db->select() - ->from($this->logTable, ['id']) - ->order(['started DESC', 'id DESC']) - ->limit(PHP_INT_MAX, 25000); + ->from($this->logTable, ['id']) + ->order(['started DESC', 'id DESC']) + ->limit(PHP_INT_MAX, 25000); $idsToDelete = $this->db->query($select)->fetchAll(PDO::FETCH_COLUMN, 0); @@ -593,7 +596,10 @@ private function clearOldLogRecords() } } - private function clearOldArchiveRecords() + /** + * @return void + */ + protected function clearOldArchiveRecords() { $archiveLogClearLimit = $this->configHelper->getArchiveLogClearLimit(); // Adding a fallback in case this configuration was not set in a consistent way @@ -607,7 +613,10 @@ private function clearOldArchiveRecords() ); } - private function unlockStackedJobs() + /** + * @return void + */ + protected function unlockStackedJobs() { $this->db->update($this->table, [ 'locked_at' => null, @@ -618,7 +627,7 @@ private function unlockStackedJobs() /** * @return bool */ - private function shouldEmptyQueue() + protected function shouldEmptyQueue() { if (getenv('PROCESS_FULL_QUEUE') && getenv('PROCESS_FULL_QUEUE') === '1') { return true; diff --git a/Observer/Insights/CheckoutCartProductAddAfter.php b/Observer/Insights/CheckoutCartProductAddAfter.php index 364244752..cc8bbda68 100644 --- a/Observer/Insights/CheckoutCartProductAddAfter.php +++ b/Observer/Insights/CheckoutCartProductAddAfter.php @@ -73,11 +73,23 @@ public function execute(Observer $observer) $userClient = $this->insightsHelper->getUserInsightsClient(); $queryId = $this->coreSession->getQueryId(); + /** Adding algolia_query_param to the items to track the conversion when product is added to the cart */ if ($this->configHelper->isClickConversionAnalyticsEnabled($storeId) && $queryId) { $conversionAnalyticsMode = $this->configHelper->getConversionAnalyticsMode($storeId); switch ($conversionAnalyticsMode) { case 'place_order': - $quoteItem->setData('algoliasearch_query_param', $queryId); + if ($product->getTypeId() == "grouped") { + $groupProducts = $product->getTypeInstance()->getAssociatedProducts($product); + foreach ($quoteItem->getQuote()->getAllItems() as $item) { + foreach ($groupProducts as $groupProduct) { + if ($groupProduct->getId() == $item->getProductId()) { + $item->setData('algoliasearch_query_param', $queryId); + } + } + } + } else { + $quoteItem->setData('algoliasearch_query_param', $queryId); + } break; case 'add_to_cart': try { @@ -91,8 +103,8 @@ public function execute(Observer $observer) $this->logger->critical($e); } } - } - + } + /** Tracking the events for add to cart when personalization is enabled */ if ($this->personalizationHelper->isPersoEnabled($storeId) && $this->personalizationHelper->isCartAddTracked($storeId) && (!$this->configHelper->isClickConversionAnalyticsEnabled($storeId) || $this->configHelper->getConversionAnalyticsMode($storeId) != 'add_to_cart')) { try { $userClient->convertedObjectIDs( @@ -105,4 +117,4 @@ public function execute(Observer $observer) } } } -} \ No newline at end of file +} diff --git a/README.md b/README.md index cf0a8fcf7..afce37437 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ Algolia Search for Magento 2 ================== -![Latest version](https://img.shields.io/badge/latest-3.9.0-green) +![Latest version](https://img.shields.io/badge/latest-3.9.1-green) ![Magento 2](https://img.shields.io/badge/Magento-2.4.x-orange) ![PHP](https://img.shields.io/badge/PHP-8.1,7.4-blue) diff --git a/composer.json b/composer.json index bed1b8c62..fafe165a8 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,7 @@ "description": "Algolia Search integration for Magento 2", "type": "magento2-module", "license": ["MIT"], - "version": "3.9.0", + "version": "3.9.1", "require": { "magento/framework": "~102.0|~103.0", "algolia/algoliasearch-client-php": "3.2", diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index 9c7178841..bef02ee3a 100755 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -1165,6 +1165,14 @@ ]]> + + + + + +
diff --git a/etc/config.xml b/etc/config.xml index 269d72f12..c53f5181e 100644 --- a/etc/config.xml +++ b/etc/config.xml @@ -55,5 +55,10 @@ + + + 10000 + + diff --git a/etc/module.xml b/etc/module.xml index 14805cd98..f58693da2 100755 --- a/etc/module.xml +++ b/etc/module.xml @@ -1,6 +1,6 @@ - + diff --git a/view/adminhtml/web/js/config.js b/view/adminhtml/web/js/config.js index 2ef66b1b5..70096033e 100644 --- a/view/adminhtml/web/js/config.js +++ b/view/adminhtml/web/js/config.js @@ -34,13 +34,19 @@ require( ]; var pageWarning = '
'; - pageWarning += '

These settings are also available in the Algolia Dashboard. We advise you to manage it from this page, because saving Magento settings will override the Algolia settings.

'; + pageWarning += '

These settings are also available in the Algolia Dashboard. We advise you to manage it from this page, cause saving Magento settings will override the Algolia settings.

'; pageWarning += '
'; + var pageWarningSynonyms = '
'; + pageWarningSynonyms += '

These settings are also available in the Algolia Dashboard. We advise you to configure synonyms configuration from the Algolia dashboard as this is going to be deprecated in release 3.9.2.

'; + pageWarningSynonyms += '
'; + for (var i=0; i < pageIds.length; i++) { var element = $(pageIds[i]); - if (element.length > 0) { + if (element.length > 0 && pageIds[i] != "#algoliasearch_synonyms_synonyms_group") { element.find('.comment').append(pageWarning); + } else if (element.length > 0 && pageIds[i] == "#algoliasearch_synonyms_synonyms_group"){ + element.find('.comment').append(pageWarningSynonyms); } } } diff --git a/view/frontend/layout/algolia_search_handle.xml b/view/frontend/layout/algolia_search_handle.xml index d41bc1e73..eb600698c 100755 --- a/view/frontend/layout/algolia_search_handle.xml +++ b/view/frontend/layout/algolia_search_handle.xml @@ -4,7 +4,6 @@ - + diff --git a/view/frontend/templates/recommend/widget/trends-item.phtml b/view/frontend/templates/recommend/widget/trends-item.phtml index d15f6c577..f2750e593 100644 --- a/view/frontend/templates/recommend/widget/trends-item.phtml +++ b/view/frontend/templates/recommend/widget/trends-item.phtml @@ -5,7 +5,7 @@ */ $trendConstainer = 'trendItems' . $block->generateUniqueToken(); ?> -
+
\ No newline at end of file + + diff --git a/view/frontend/web/autocomplete.js b/view/frontend/web/autocomplete.js index 0ee6a13fd..1478dbdf7 100755 --- a/view/frontend/web/autocomplete.js +++ b/view/frontend/web/autocomplete.js @@ -1,27 +1,29 @@ -let algoliaAutocomplete; -let suggestionSection = false; -let algoliaFooter; -let productResult = []; -requirejs(['algoliaBundle', 'pagesHtml', 'categoriesHtml', 'productsHtml', 'suggestionsHtml', 'additionalHtml', 'domReady!'], function(algoliaBundle, pagesHtml, categoriesHtml, productsHtml, suggestionsHtml, additionalHtml) { - algoliaAutocomplete = algoliaBundle; - algoliaBundle.$(function ($) { +define( + ['algoliaBundle', 'pagesHtml', 'categoriesHtml', 'productsHtml', 'suggestionsHtml', 'additionalHtml', 'domReady!'], + function(algoliaBundle, pagesHtml, categoriesHtml, productsHtml, suggestionsHtml, additionalHtml) { - /** We have nothing to do here if autocomplete is disabled **/ - if (!algoliaConfig.autocomplete.enabled) { - return; - } + const DEFAULT_HITS_PER_SECTION = 2; + + let suggestionSection = false; + let algoliaFooter; + + /** We have nothing to do here if autocomplete is disabled **/ + if (!algoliaConfig.autocomplete.enabled) { + return; + } + algoliaBundle.$(function ($) { /** * Initialise Algolia client * Docs: https://www.algolia.com/doc/api-client/getting-started/instantiate-client-index/ **/ - var algolia_client = algoliaBundle.algoliasearch(algoliaConfig.applicationId, algoliaConfig.apiKey); + const algolia_client = algoliaBundle.algoliasearch(algoliaConfig.applicationId, algoliaConfig.apiKey); algolia_client.addAlgoliaAgent('Magento2 integration (' + algoliaConfig.extensionVersion + ')'); - var searchClient = algoliaBundle.algoliasearch(algoliaConfig.applicationId, algoliaConfig.apiKey); + const searchClient = algoliaBundle.algoliasearch(algoliaConfig.applicationId, algoliaConfig.apiKey); // autocomplete code moved from common.js to autocomplete.js - window.transformAutocompleteHit = function (hit, price_key, $, helper) { + const transformAutocompleteHit = function (hit, price_key, helper) { if (Array.isArray(hit.categories)) hit.categories = hit.categories.join(', '); @@ -33,11 +35,12 @@ requirejs(['algoliaBundle', 'pagesHtml', 'categoriesHtml', 'productsHtml', 'sugg hit.categories_without_path = hit.categories_without_path.join(', '); } - var matchedColors = []; + let matchedColors = []; + // TODO: Adapt this migrated code from common.js - helper not utilized if (helper && algoliaConfig.useAdaptiveImage === true) { if (hit.images_data && helper.state.facetsRefinements.color) { - matchedColors = helper.state.disjunctiveFacetsRefinements.color.slice(0); // slice to clone + matchedColors = helper.state.facetsRefinements.color.slice(0); // slice to clone } if (hit.images_data && helper.state.disjunctiveFacetsRefinements.color) { @@ -46,7 +49,7 @@ requirejs(['algoliaBundle', 'pagesHtml', 'categoriesHtml', 'productsHtml', 'sugg } if (Array.isArray(hit.color)) { - var colors = []; + let colors = []; $.each(hit._highlightResult.color, function (i, color) { if (color.matchLevel === undefined || color.matchLevel === 'none') { @@ -56,7 +59,7 @@ requirejs(['algoliaBundle', 'pagesHtml', 'categoriesHtml', 'productsHtml', 'sugg colors.push(color.value); if (algoliaConfig.useAdaptiveImage === true) { - var matchedColor = color.matchedWords.join(' '); + const matchedColor = color.matchedWords.join(' '); if (hit.images_data && color.fullyHighlighted && color.fullyHighlighted === true) { matchedColors.push(matchedColor); } @@ -102,7 +105,7 @@ requirejs(['algoliaBundle', 'pagesHtml', 'categoriesHtml', 'productsHtml', 'sugg if (hit['price'][algoliaConfig.currencyCode]['default_original_formated'] && hit['price'][algoliaConfig.currencyCode]['special_to_date']) { - var priceExpiration = hit['price'][algoliaConfig.currencyCode]['special_to_date']; + const priceExpiration = hit['price'][algoliaConfig.currencyCode]['special_to_date']; if (algoliaConfig.now > priceExpiration + 1) { hit['price'][algoliaConfig.currencyCode]['default_formated'] = hit['price'][algoliaConfig.currencyCode]['default_original_formated']; @@ -112,9 +115,9 @@ requirejs(['algoliaBundle', 'pagesHtml', 'categoriesHtml', 'productsHtml', 'sugg } // Add to cart parameters - var action = algoliaConfig.instant.addToCartParams.action + 'product/' + hit.objectID + '/'; + const action = algoliaConfig.instant.addToCartParams.action + 'product/' + hit.objectID + '/'; - var correctFKey = getCookie('form_key'); + const correctFKey = getCookie('form_key'); if(correctFKey != "" && algoliaConfig.instant.addToCartParams.formKey != correctFKey) { algoliaConfig.instant.addToCartParams.formKey = correctFKey; @@ -132,7 +135,7 @@ requirejs(['algoliaBundle', 'pagesHtml', 'categoriesHtml', 'productsHtml', 'sugg if (algoliaConfig.ccAnalytics.enabled && algoliaConfig.ccAnalytics.conversionAnalyticsMode !== 'disabled') { - var insightsDataUrlString = $.param({ + const insightsDataUrlString = $.param({ queryID: hit.__autocomplete_queryID, objectID: hit.objectID, indexName: hit.__autocomplete_indexName @@ -148,18 +151,15 @@ requirejs(['algoliaBundle', 'pagesHtml', 'categoriesHtml', 'productsHtml', 'sugg return hit; }; - window.getAutocompleteSource = function (section, algolia_client, $, i) { - if (section.hitsPerPage <= 0) - return null; - - var options = { - hitsPerPage: section.hitsPerPage, + const getAutocompleteSource = function (section, algolia_client, i) { + let options = { + hitsPerPage: section.hitsPerPage || DEFAULT_HITS_PER_SECTION, analyticsTags: 'autocomplete', clickAnalytics: true, distinct: true }; - var source; + let source; if (section.name === "products") { options.facets = ['categories.level0']; @@ -172,60 +172,53 @@ requirejs(['algoliaBundle', 'pagesHtml', 'categoriesHtml', 'productsHtml', 'sugg name: section.name, hitsPerPage: section.hitsPerPage, paramName:algolia_client.initIndex(algoliaConfig.indexName + "_" + section.name), - options:options, + options, templates: { - noResults() { - return productsHtml.getNoResultHtml(); + noResults({html}) { + return productsHtml.getNoResultHtml({html}); + }, + header({items, html}) { + return productsHtml.getHeaderHtml({items, html}) }, item({ item, components, html }) { if(suggestionSection){ - algoliaAutocomplete.$('.aa-Panel').addClass('productColumn2'); - algoliaAutocomplete.$('.aa-Panel').removeClass('productColumn1'); + $('.aa-Panel').addClass('productColumn2'); + $('.aa-Panel').removeClass('productColumn1'); }else{ - algoliaAutocomplete.$('.aa-Panel').removeClass('productColumn2'); - algoliaAutocomplete.$('.aa-Panel').addClass('productColumn1'); + $('.aa-Panel').removeClass('productColumn2'); + $('.aa-Panel').addClass('productColumn1'); } - if(algoliaFooter && algoliaFooter !== undefined && algoliaFooter !== null && algoliaAutocomplete.$('#algoliaFooter').length === 0){ - algoliaAutocomplete.$('.aa-PanelLayout').append(algoliaFooter); + if(algoliaFooter && algoliaFooter !== undefined && algoliaFooter !== null && $('#algoliaFooter').length === 0){ + $('.aa-PanelLayout').append(algoliaFooter); } - var _data = transformAutocompleteHit(item, algoliaConfig.priceKey, $); - return productsHtml.getProductsHtml(_data, components, html); + const _data = transformAutocompleteHit(item, algoliaConfig.priceKey); + return productsHtml.getItemHtml({ item: _data, components, html }); }, - footer({html}) { - var keys = []; - for (var i = 0; i { + const url = resultDetails.allDepartmentsUrl + '&categories=' + encodeURIComponent(key); + return { + name: key, + value: firstItem.allCategories[key], + url + }; }); } + //reverse value sort apparently... + allCategories.sort((a, b) => b.value - a.value); + resultDetails.allCategories = allCategories.slice(0, 2); } } - - keys.sort(function (a, b) { - return b.value - a.value; - }); - - var orsTab = []; - - if (keys.length > 0) { - orsTab = []; - for (var i = 0; i < keys.length && i < 2; i++) { - orsTab.push( - { - url:keys[i].url, - name:keys[i].key - } - ); - } - } - - var allUrl = algoliaConfig.baseUrl + '/catalogsearch/result/?q=' + encodeURIComponent(productResult[0].query); - return productsHtml.getFooterHtml(html, orsTab, allUrl, productResult) + return productsHtml.getFooterHtml({ html, ...resultDetails }); } } }; @@ -239,16 +232,19 @@ requirejs(['algoliaBundle', 'pagesHtml', 'categoriesHtml', 'productsHtml', 'sugg name: section.name || i, hitsPerPage: section.hitsPerPage, paramName:algolia_client.initIndex(algoliaConfig.indexName + "_" + section.name), - options:options, + options, templates: { - noResults() { - return categoriesHtml.getNoResultHtml(); + noResults({html}) { + return categoriesHtml.getNoResultHtml({html}); }, - header() { - return categoriesHtml.getHeaderHtml(section); + header({html, items}) { + return categoriesHtml.getHeaderHtml({section, html, items}); }, item({ item, components, html }) { - return categoriesHtml.getCategoriesHtml(item, components, html); + return categoriesHtml.getItemHtml({item, components, html}); + }, + footer({html, items}) { + return categoriesHtml.getFooterHtml({section, html, items}); } } }; @@ -259,36 +255,32 @@ requirejs(['algoliaBundle', 'pagesHtml', 'categoriesHtml', 'productsHtml', 'sugg name: section.name || i, hitsPerPage: section.hitsPerPage, paramName:algolia_client.initIndex(algoliaConfig.indexName + "_" + section.name), - options:options, + options, templates: { - noResults() { - return pagesHtml.getNoResultHtml(); + noResults({html}) { + return pagesHtml.getNoResultHtml({html}); }, - header() { - return pagesHtml.getHeaderHtml(section); + header({html, items}) { + return pagesHtml.getHeaderHtml({section, html, items}); }, - item({ item, components, html }) { - return pagesHtml.getPagesHtml(item, components, html); + item({item, components, html}) { + return pagesHtml.getItemHtml({item, components, html}); + }, + footer({html, items}) { + return pagesHtml.getFooterHtml({section, html, items}); } } }; } else if (section.name === "suggestions") { - var suggestions_index = algolia_client.initIndex(algoliaConfig.indexName + "_suggestions"); - var products_index = algolia_client.initIndex(algoliaConfig.indexName + "_products"); - + const suggestions_index = algolia_client.initIndex(algoliaConfig.indexName + "_suggestions"); source = { displayKey: 'query', name: section.name, hitsPerPage: section.hitsPerPage, paramName: suggestions_index, - options:options, - templates: { - item({ item, html }) { - return html`
Suggestion List
`; - } - } + options }; } else { /** If is not products, categories, pages or suggestions, it's additional section **/ @@ -297,16 +289,19 @@ requirejs(['algoliaBundle', 'pagesHtml', 'categoriesHtml', 'productsHtml', 'sugg displayKey: 'value', name: section.name || i, hitsPerPage: section.hitsPerPage, - options:options, + options, templates: { - noResults() { - return additionalHtml.getNoResultHtml(); + noResults({html}) { + return additionalHtml.getNoResultHtml({html}); }, - header() { - return additionalHtml.getHeaderHtml(section); + header({html, items}) { + return additionalHtml.getHeaderHtml({section, html, items}); }, item({ item, components, html }) { - return additionalHtml.getAdditionalHtml(item, components, html); + return additionalHtml.getItemHtml({item, components, html, section}); + }, + footer({html, items}) { + return additionalHtml.getFooterHtml({section, html, items}); } } }; @@ -314,6 +309,7 @@ requirejs(['algoliaBundle', 'pagesHtml', 'categoriesHtml', 'productsHtml', 'sugg return source; }; + /** Add products and categories that are required sections **/ /** Add autocomplete menu sections **/ if (algoliaConfig.autocomplete.nbOfProductsSuggestions > 0) { @@ -333,19 +329,21 @@ requirejs(['algoliaBundle', 'pagesHtml', 'categoriesHtml', 'productsHtml', 'sugg * For autocomplete feature is used Algolia's autocomplete.js library * Docs: https://github.com/algolia/autocomplete.js **/ - $(algoliaConfig.autocomplete.selector).each(function (i) { + $(algoliaConfig.autocomplete.selector).each(function () { let querySuggestionsPlugin = ""; - var options = { + let sources = []; + let autocompleteConfig = []; + let options = { container: '#algoliaAutocomplete', placeholder: 'Search for products, categories, ...', debug: algoliaConfig.autocomplete.isDebugEnabled, detachedMediaQuery: 'none', onSubmit(data){ if(data.state.query && data.state.query !== null && data.state.query !== ""){ - window.location.href = `/catalogsearch/result/?q=${data.state.query}`; + window.location.href = algoliaConfig.resultPageUrl+`?q=${data.state.query}`; } }, - getSources({query, setContext}) { + getSources() { return autocompleteConfig; }, }; @@ -356,10 +354,10 @@ requirejs(['algoliaBundle', 'pagesHtml', 'categoriesHtml', 'productsHtml', 'sugg } if (algoliaConfig.removeBranding === false) { - algoliaFooter = ''; + algoliaFooter = ``; } - sources = algolia.triggerHooks('beforeAutocompleteSources', algoliaConfig.autocomplete.sections, algolia_client, algoliaBundle); + sources = algolia.triggerHooks('beforeAutocompleteSources', sources, algolia_client, algoliaBundle); options = algolia.triggerHooks('beforeAutocompleteOptions', options); // Keep for backward compatibility @@ -368,22 +366,23 @@ requirejs(['algoliaBundle', 'pagesHtml', 'categoriesHtml', 'productsHtml', 'sugg 'Please, replace your hook method with new hook API. ' + 'More information you can find on https://www.algolia.com/doc/integration/magento-2/customize/custom-front-end-events/'); - var hookResult = algoliaHookBeforeAutocompleteStart(sources, options, algolia_client); + const hookResult = algoliaHookBeforeAutocompleteStart(sources, options, algolia_client); sources = hookResult.shift(); options = hookResult.shift(); } /** Setup autocomplete data sources **/ - var sources = [], - i = 0; - $.each(algoliaConfig.autocomplete.sections, function (name, section) { - var source = getAutocompleteSource(section, algolia_client, $, i); + let i = 0; + $.each(algoliaConfig.autocomplete.sections, function (...[, section]) { + const source = getAutocompleteSource(section, algolia_client, i); if (source) { sources.push(source); } + /** TODO: Review this block - appears to only apply to Autocomplete v0 with Hogan templates which is now unsupported + * e.g. view/frontend/templates/autocomplete/menu.phtml **/ /** Those sections have already specific placeholder, * so do not use the default aa-dataset-{i} class to specify the placeholder **/ if (section.name !== 'suggestions' && section.name !== 'products') { @@ -391,29 +390,30 @@ requirejs(['algoliaBundle', 'pagesHtml', 'categoriesHtml', 'productsHtml', 'sugg } }); - let autocompleteConfig = []; sources.forEach(function(data){ if(data.name === "suggestions"){ suggestionSection = true; - querySuggestionsPlugin = algoliaAutocomplete.createQuerySuggestionsPlugin.createQuerySuggestionsPlugin({ + querySuggestionsPlugin = algoliaBundle.createQuerySuggestionsPlugin.createQuerySuggestionsPlugin({ searchClient, indexName: data.paramName.indexName, transformSource({ source }) { return { ...source, getItemUrl({ item }) { - return `/search?q=${item.query}`; + return algoliaConfig.resultPageUrl+`?q=${item.query}`; }, templates: { - noResults() { - return 'No results.'; + noResults({html}) { + return suggestionsHtml.getNoResultHtml({html}); + }, + header({html, items}) { + return suggestionsHtml.getHeaderHtml({section: data, html, items}); }, - header() { - return sources[0].name; + item({item, html}) { + return suggestionsHtml.getItemHtml({item, html}) }, - item(params) { - const { item, html } = params; - return suggestionsHtml.getSuggestionsHtml(item, html) + footer({html, items}) { + return suggestionsHtml.getFooterHtml({section: data, html, items}) }, }, }; @@ -432,9 +432,20 @@ requirejs(['algoliaBundle', 'pagesHtml', 'categoriesHtml', 'productsHtml', 'sugg params: data.options, }, ], + // Stash additional data at item level transformResponse({ results, hits }) { - productResult = results; - return hits; + const resDetail = results[0]; + + return hits.map(res => { + return res.map(hit => { + return { + ...hit, + nbHits: resDetail.nbHits, + allCategories: resDetail.facets['categories.level0'], + query: resDetail.query + } + }) + }); }, }); }, @@ -453,6 +464,20 @@ requirejs(['algoliaBundle', 'pagesHtml', 'categoriesHtml', 'productsHtml', 'sugg params: data.options, }, ], + // Stash additional data at item level + transformResponse({ results, hits }) { + const resDetail = results[0]; + + return hits.map(res => { + return res.map(hit => { + return { + ...hit, + query: resDetail.query + } + }) + }); + }, + }); }, templates: data.templates, @@ -462,18 +487,21 @@ requirejs(['algoliaBundle', 'pagesHtml', 'categoriesHtml', 'productsHtml', 'sugg options.plugins = [querySuggestionsPlugin]; /** Bind autocomplete feature to the input */ - var algoliaAutocompleteInstance = algoliaAutocomplete.autocomplete(options); + let algoliaAutocompleteInstance = algoliaBundle.autocomplete(options); algoliaAutocompleteInstance = algolia.triggerHooks('afterAutocompleteStart', algoliaAutocompleteInstance); //Autocomplete insight click conversion if (algoliaConfig.ccAnalytics.enabled && algoliaConfig.ccAnalytics.conversionAnalyticsMode !== 'disabled') { - jQuery(document).on('click', '.algoliasearch-autocomplete-hit', function(){ - var $this = $(this); + $(document).on('click', '.algoliasearch-autocomplete-hit', function(){ + const $this = $(this); if ($this.data('clicked')) return; - let itemUrl = jQuery(this).attr('href'); + + let objectId = $this.attr('data-objectId'); + let indexName = $this.attr('data-index'); + let queryId = $this.attr('data-queryId'); let eventData = algoliaInsights.buildEventData( - 'Clicked', getHitsUrlParameter(itemUrl, 'objectID'), getHitsUrlParameter(itemUrl, 'indexName'), 1, getHitsUrlParameter(itemUrl, 'queryID') + 'Clicked', objectId, indexName, 1, queryId ); algoliaInsights.trackClick(eventData); $this.attr('data-clicked', true); @@ -481,11 +509,4 @@ requirejs(['algoliaBundle', 'pagesHtml', 'categoriesHtml', 'productsHtml', 'sugg } }); }); - - function getHitsUrlParameter(url, name) { - name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); - var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), - results = regex.exec(url); - return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); - } }); diff --git a/view/frontend/web/images/algolia-logo-blue.svg b/view/frontend/web/images/algolia-logo-blue.svg new file mode 100644 index 000000000..44e9942d1 --- /dev/null +++ b/view/frontend/web/images/algolia-logo-blue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/view/frontend/web/internals/autocomplete.css b/view/frontend/web/internals/autocomplete.css index fc7fb1926..c08358b8a 100755 --- a/view/frontend/web/internals/autocomplete.css +++ b/view/frontend/web/internals/autocomplete.css @@ -1,4 +1,7 @@ /** Auto-completion menu */ +html { + --autocomplete-muted-color: #969faf; +} #algolia-autocomplete-container .aa-dropdown-menu .before_special, .aa-Panel .before_special { color: #aaaaaa; @@ -370,6 +373,19 @@ color: #222; } +.algoliasearch-autocomplete-hit .info .algoliasearch-autocomplete-category span:nth-child(n+2):before { + content: " | "; +} + +.algoliasearch-autocomplete-hit .info .algoliasearch-autocomplete-price .after_special:after { + content: " "; +} + +.algoliasearch-autocomplete-hit .info .algoliasearch-autocomplete-price .tier_price { + padding-left: 5px; +} + + #algolia-autocomplete-container .aa-dropdown-menu .footer_algolia { position: absolute; width: 100%; @@ -504,7 +520,22 @@ .footer_algolia{ position: absolute; bottom: 0; + display: flex; + flex-wrap: nowrap; + flex-direction: row; + align-items: flex-start; +} + +.footer_algolia .algolia-search-by-label { + color: var(--autocomplete-muted-color); + font-size: .9em; + margin-right: 5px; +} + +.footer_algolia img { + min-width: 80px; } + @media (min-width: 992px) { .aa-Panel.productColumn2 .aa-PanelLayout section:nth-child(2), .aa-Panel.productColumn1 .aa-PanelLayout section:nth-child(1){ margin-left: 2.5rem !important; @@ -567,8 +598,15 @@ } @media (max-width: 768px) { - .aa-Panel .aa-PanelLayout, .footer_algolia{ + .aa-Panel .aa-PanelLayout { + justify-content: start; + width:100%; + display: flex; + flex-direction: column; + } + .footer_algolia{ width: 100%; + justify-content: center; } .aa-Panel .aa-List { padding-left: 0px; diff --git a/view/frontend/web/internals/common.js b/view/frontend/web/internals/common.js index 542f646f1..ec2b85184 100755 --- a/view/frontend/web/internals/common.js +++ b/view/frontend/web/internals/common.js @@ -86,7 +86,7 @@ requirejs(['algoliaBundle'], function(algoliaBundle) { if (helper && algoliaConfig.useAdaptiveImage === true) { if (hit.images_data && helper.state.facetsRefinements.color) { - matchedColors = helper.state.disjunctiveFacetsRefinements.color.slice(0); // slice to clone + matchedColors = helper.state.facetsRefinements.color.slice(0); // slice to clone } if (hit.images_data && helper.state.disjunctiveFacetsRefinements.color) { @@ -142,20 +142,20 @@ requirejs(['algoliaBundle'], function(algoliaBundle) { if (Array.isArray(hit._highlightResult.name)) hit._highlightResult.name = hit._highlightResult.name[0]; - if (Array.isArray(hit.price)) + if (Array.isArray(hit.price)) { hit.price = hit.price[0]; + if (hit['price'] !== undefined && price_key !== '.' + algoliaConfig.currencyCode + '.default' && hit['price'][algoliaConfig.currencyCode][price_key.substr(1) + '_formated'] !== hit['price'][algoliaConfig.currencyCode]['default_formated']) { + hit['price'][algoliaConfig.currencyCode][price_key.substr(1) + '_original_formated'] = hit['price'][algoliaConfig.currencyCode]['default_formated']; + } - if (hit['price'] !== undefined && price_key !== '.' + algoliaConfig.currencyCode + '.default' && hit['price'][algoliaConfig.currencyCode][price_key.substr(1) + '_formated'] !== hit['price'][algoliaConfig.currencyCode]['default_formated']) { - hit['price'][algoliaConfig.currencyCode][price_key.substr(1) + '_original_formated'] = hit['price'][algoliaConfig.currencyCode]['default_formated']; - } - - if (hit['price'][algoliaConfig.currencyCode]['default_original_formated'] - && hit['price'][algoliaConfig.currencyCode]['special_to_date']) { - var priceExpiration = hit['price'][algoliaConfig.currencyCode]['special_to_date']; + if (hit['price'][algoliaConfig.currencyCode]['default_original_formated'] + && hit['price'][algoliaConfig.currencyCode]['special_to_date']) { + var priceExpiration = hit['price'][algoliaConfig.currencyCode]['special_to_date']; - if (algoliaConfig.now > priceExpiration + 1) { - hit['price'][algoliaConfig.currencyCode]['default_formated'] = hit['price'][algoliaConfig.currencyCode]['default_original_formated']; - hit['price'][algoliaConfig.currencyCode]['default_original_formated'] = false; + if (algoliaConfig.now > priceExpiration + 1) { + hit['price'][algoliaConfig.currencyCode]['default_formated'] = hit['price'][algoliaConfig.currencyCode]['default_original_formated']; + hit['price'][algoliaConfig.currencyCode]['default_original_formated'] = false; + } } } @@ -323,7 +323,7 @@ requirejs(['algoliaBundle'], function(algoliaBundle) { return protocol + '//' + hostname + portWithPrefix + pathname; } else { - if (queryString) { + if (queryString && queryString != 'q=__empty__') { return protocol + '//' + hostname + portWithPrefix + pathname + '?' + queryString; } else { return protocol + '//' + hostname + portWithPrefix + pathname; diff --git a/view/frontend/web/internals/template/autocomplete/additional-section.js b/view/frontend/web/internals/template/autocomplete/additional-section.js index ed161b942..354c9f2d7 100644 --- a/view/frontend/web/internals/template/autocomplete/additional-section.js +++ b/view/frontend/web/internals/template/autocomplete/additional-section.js @@ -1,15 +1,23 @@ define([], function () { return { - getAdditionalHtml: function (item, components, html) { - return html`${components.Highlight({ hit: item, attribute: 'value' })}`; + getNoResultHtml: function ({html}) { + return html`

No Results

`; }, - getHeaderHtml: function (section) { - return section.name; + getHeaderHtml: function ({section}) { + return section.label || section.name; }, - getNoResultHtml: function () { - return 'No Results'; + getItemHtml: function ({item, components, html, section}) { + return html` + ${components.Highlight({ hit: item, attribute: 'value' })} + `; + + }, + + getFooterHtml: function () { + return ""; } }; }); diff --git a/view/frontend/web/internals/template/autocomplete/categories.js b/view/frontend/web/internals/template/autocomplete/categories.js index 090beafd1..7806ab35f 100644 --- a/view/frontend/web/internals/template/autocomplete/categories.js +++ b/view/frontend/web/internals/template/autocomplete/categories.js @@ -1,17 +1,22 @@ define([], function () { return { - getCategoriesHtml: function (item, components, html) { - return html ` - ${components.Highlight({ hit: item, attribute: 'path' })} (${item.product_count}) - `; + getNoResultHtml: function ({html}) { + return html`

No Results

`; }, - getHeaderHtml: function (section) { + getHeaderHtml: function ({section}) { return section.name; }, - getNoResultHtml: function () { - return 'No Results'; - } + getItemHtml: function ({item, components, html}) { + return html ` + ${components.Highlight({ hit: item, attribute: 'path' })} (${item.product_count}) + `; + }, + + getFooterHtml: function () { + return ""; + }, }; }); diff --git a/view/frontend/web/internals/template/autocomplete/pages.js b/view/frontend/web/internals/template/autocomplete/pages.js index 858059a80..b63e05047 100644 --- a/view/frontend/web/internals/template/autocomplete/pages.js +++ b/view/frontend/web/internals/template/autocomplete/pages.js @@ -1,7 +1,16 @@ define([], function () { return { - getPagesHtml: function (item, components, html) { - return html` + getNoResultHtml: function ({html}) { + return html`

No Results

`; + }, + + getHeaderHtml: function ({section}) { + return section.label || section.name; + }, + + getItemHtml: function ({item, components, html}) { + return html`
${components.Highlight({hit: item, attribute: 'name'})}
@@ -12,12 +21,8 @@ define([], function () { `; }, - getHeaderHtml: function (section) { - return section.name; - }, - - getNoResultHtml: function () { - return 'No Results'; + getFooterHtml: function () { + return ""; } }; }); diff --git a/view/frontend/web/internals/template/autocomplete/products.js b/view/frontend/web/internals/template/autocomplete/products.js index b6ea27fdb..e673d10ee 100644 --- a/view/frontend/web/internals/template/autocomplete/products.js +++ b/view/frontend/web/internals/template/autocomplete/products.js @@ -1,72 +1,100 @@ define([], function () { return { - getProductsHtml: function (_data, components, html) { - var color = ''; - if (_data._highlightResult.color !== undefined) { - color = _data._highlightResult.color.value; - } - var origFormatedVar = algoliaConfig.origFormatedVar; - var tierFormatedvar = algoliaConfig.tierFormatedVar; - if (algoliaConfig.priceGroup == null) { - return html` -
${_data.name || ''}
-
- ${components.Highlight({hit: _data, attribute: 'name'}) || ''} -
- ${color && color != '' ? html `color : ${components.Highlight({hit: _data, attribute: 'color'})}` : - _data.categories_without_path && _data.categories_without_path.length != 0 ? html `in ${components.Highlight({hit: _data, attribute: 'categories_without_path'})}` : ''} -
- ${_data['price'] !== undefined ? html `
- - ${_data['price'][algoliaConfig.currencyCode]['default_formated']} - - ${_data['price'][algoliaConfig.currencyCode]['default_original_formated'] != null ? html` - ${_data['price'][algoliaConfig.currencyCode]['default_original_formated']}` : ''} -
` : ''} -
-
`; - } else { - return html` -
${_data.name || ''}
-
- ${components.Highlight({hit: _data, attribute: 'name'}) || ''} -
- ${color && color != '' ? html `color : ${components.Highlight({hit: _data, attribute: 'color'})}` : - _data.categories_without_path && _data.categories_without_path.length != 0 ? html `in ${components.Highlight({hit: _data, attribute: 'categories_without_path'})}` : ''} -
- ${_data['price'] !== undefined ? html `
- - ${_data['price'][algoliaConfig.currencyCode][algoliaConfig.priceGroup + '_formated']} - - ${_data['price'][algoliaConfig.currencyCode][algoliaConfig.priceGroup + '_original_formated'] != null ? html` - ${_data['price'][algoliaConfig.currencyCode][algoliaConfig.priceGroup + '_original_formated']}` : ''} - - ${_data['price'][algoliaConfig.currencyCode][algoliaConfig.priceGroup + '_tier_formated'] != null ? html` - As low as ${_data['price'][algoliaConfig.currencyCode][algoliaConfig.priceGroup + '_tier_formated']}` : ''} -
` : ''} + + //////////////////// + // Template API // + //////////////////// + + getNoResultHtml: function ({html}) { + return html`

No Results

`; + }, + + getHeaderHtml: function () { + return ""; + }, + + getItemHtml: function ({item, components, html}) { + return html`
+
${item.name || ''}
+
+ `; }, - getHeaderHtml: function (section) { - return section.name; + getFooterHtml: function ({html, ...resultDetails}) { + return html``; }, - getNoResultHtml: function () { - return 'No Results'; + //////////////////// + // Helper methods // + //////////////////// + + getColorHtml: (item, components, html) => { + if (item._highlightResult.color == undefined || item._highlightResult.color.value == "") return ""; + + return html`color: ${components.Highlight({ hit: item, attribute: "color" })}`; }, - getFooterHtml: function (html, orsTab, allUrl, productResult) { - if(orsTab && orsTab.length > 0 && algoliaConfig.instant.enabled) { - return html ``; - }else{ - return html ``; - } + getCategoriesHtml: (item, components, html) => { + if (item.categories_without_path == undefined || item.categories_without_path.length == 0) return ""; + + return html`in ${components.Highlight({ hit: item, attribute: "categories_without_path",})}`; + }, + + getOriginalPriceHtml: (item, html, priceGroup) => { + if (item['price'][algoliaConfig.currencyCode][priceGroup + '_original_formated'] == null) return ""; + + return html` ${item['price'][algoliaConfig.currencyCode][priceGroup + '_original_formated']} `; + }, + + getTierPriceHtml: (item, html, priceGroup) => { + if (item['price'][algoliaConfig.currencyCode][priceGroup + '_tier_formated'] == null) return ""; + + return html` As low as ${item['price'][algoliaConfig.currencyCode][priceGroup + '_tier_formated']}`; + }, + + getPricingHtml: function(item, html) { + if (item['price'] == undefined) return ""; + + const priceGroup = algoliaConfig.priceGroup || 'default'; + + return html `
+ + ${item['price'][algoliaConfig.currencyCode][priceGroup + '_formated']} + + ${this.getOriginalPriceHtml(item, html, priceGroup)} + + ${this.getTierPriceHtml(item, html, priceGroup)} +
`; + }, + + getFooterSearchCategoryLinks: (html, resultDetails) => { + if (resultDetails.allCategories == undefined || resultDetails.allCategories.length === 0) return ""; + + return html ` ${algoliaConfig.translations.orIn} + ${resultDetails.allCategories.map((list, index) => + index === 0 ? html` ${list.name}` : html`, ${list.name}` + )} + `; + }, + + getFooterSearchLinks: function(html, resultDetails) { + if (resultDetails.nbHits === 0) return ""; + + return html`${algoliaConfig.translations.seeIn} ${algoliaConfig.translations.allDepartments} (${resultDetails.nbHits}) + ${this.getFooterSearchCategoryLinks(html, resultDetails)} + `; } + }; }); diff --git a/view/frontend/web/internals/template/autocomplete/suggestions.js b/view/frontend/web/internals/template/autocomplete/suggestions.js index 3b1d86344..496bd5b27 100644 --- a/view/frontend/web/internals/template/autocomplete/suggestions.js +++ b/view/frontend/web/internals/template/autocomplete/suggestions.js @@ -1,17 +1,22 @@ define([], function () { return { - getSuggestionsHtml: function (item, html) { - return html` - ${item.query} - `; + getNoResultHtml: function ({html}) { + return html`

No Results

`; }, - getPagesHeaderHtml: function (section) { + getHeaderHtml: function ({section}) { return section.name; }, - getNoResultHtml: function () { - return 'No Results'; + getItemHtml: function ({item, html}) { + return html` + ${item.query} + `; + }, + + getFooterHtml: function () { + return ""; } }; }); diff --git a/view/frontend/web/recommend.js b/view/frontend/web/recommend.js index 7c32e4e23..4921e54f4 100644 --- a/view/frontend/web/recommend.js +++ b/view/frontend/web/recommend.js @@ -119,11 +119,13 @@ requirejs([ {className: "recommend-item product-url", href: item.url, 'data-objectId': item.objectID, 'data-index': this.defaultIndexName}, createElement('img', {className: "product-img", src: item.image_url}, item.image_url), createElement('p', {className: "product-name"}, item.name), - createElement('form', {className: 'addTocartForm', action: action, method: 'post'}, + createElement('form', {className: 'addTocartForm', action: action, method: 'post', 'data-role':'tocart-form'}, createElement('input', {type: 'hidden', name: 'form_key',value: config.recommend.addToCartParams.formKey}), createElement('input', {type: 'hidden', name:'unec', value: AlgoliaBase64.mageEncode(action)}), createElement('input', {type: 'hidden', name:'product', value: item.objectID}), - createElement('button', {type: 'submit', className: 'action tocart primary'}, $tr('Add To Cart')) + createElement('button', {type: 'submit', className: 'action tocart primary'}, [ + createElement('span', {}, $tr('Add To Cart')) + ]) ) ) )