diff --git a/Block/Cart/Recommend.php b/Block/Cart/Recommend.php new file mode 100644 index 000000000..884f64831 --- /dev/null +++ b/Block/Cart/Recommend.php @@ -0,0 +1,54 @@ +checkoutSession = $checkoutSession; + $this->configHelper = $configHelper; + } + + /** + * @return array + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function getAllCartItems() + { + $cartItems = array(); + $itemCollection = $this->checkoutSession->getQuote()->getAllVisibleItems(); + foreach ( $itemCollection as $item) { + $cartItems[] = $item->getProductId(); + } + return array_unique($cartItems); + } + +} diff --git a/Block/Configuration.php b/Block/Configuration.php index c5f41b524..61d1cbd39 100755 --- a/Block/Configuration.php +++ b/Block/Configuration.php @@ -165,8 +165,20 @@ public function getConfiguration() 'recommend' => [ 'enabledFBT' => $config->isRecommendFrequentlyBroughtTogetherEnabled(), 'enabledRelated' => $config->isRecommendRelatedProductsEnabled(), + 'enabledFBTInCart' => $config->isRecommendFrequentlyBroughtTogetherEnabledOnCartPage(), + 'enabledRelatedInCart' => $config->isRecommendRelatedProductsEnabledOnCartPage(), 'limitFBTProducts' => $config->getNumberOfFrequentlyBoughtTogetherProducts(), 'limitRelatedProducts' => $config->getNumberOfRelatedProducts(), + 'limitTrendingItems' => $config->getNumberOfTrendingItems(), + 'enabledTrendItems' => $config->isRecommendTrendingItemsEnabled(), + 'trendItemFacetName' => $config->getTrendingItemsFacetName(), + 'trendItemFacetValue' => $config->getTrendingItemsFacetValue(), + 'isTrendItemsEnabledInPDP' => $config->isTrendItemsEnabledInPDP(), + 'isTrendItemsEnabledInCartPage' => $config->isTrendItemsEnabledInShoppingCart(), + 'isAddToCartEnabledInFBT' => $config->isAddToCartEnabledInFrequentlyBoughtTogether(), + 'isAddToCartEnabledInRelatedProduct' => $config->isAddToCartEnabledInRelatedProducts(), + 'isAddToCartEnabledInTrendsItem' => $config->isAddToCartEnabledInTrendsItem(), + 'addToCartParams' => $addToCartParams, ], 'extensionVersion' => $config->getExtensionVersion(), 'applicationId' => $config->getApplicationID(), diff --git a/Block/Widget/TrendsItem.php b/Block/Widget/TrendsItem.php new file mode 100644 index 000000000..08dd0f336 --- /dev/null +++ b/Block/Widget/TrendsItem.php @@ -0,0 +1,39 @@ +mathRandom = $mathRandom; + parent::__construct( + $context, + $data + ); + } + + /** + * @return string + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function generateUniqueToken() + { + return $this->mathRandom->getRandomString(5); + } +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 96212af2e..d97f37bc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,30 @@ # CHANGE LOG +## 3.9.0 + +### New Features +- Trends Recommendations: We have added the ability to add Trending Items to the PDP and the shopping cart page. More information can be found here. We also provide a Trending Items widget that can be used to add Trending Items to any page. +- Added an option to show Recommend Related and Frequently Bought Together products on the shopping cart page. +- Added an option to enable the Add To Cart button for all types of recommended products (Related, Frequently Bought Together, and Trending Items). +- Added Algolia Recommend dashboard link on the Magento dashboard +- Added Algolia Search extensions release notes link in the Magento admin to be able to access release notes easily. +- Implemented Recommended Product click event using personalization. + +### UPDATES +- Refactored the Algolia Insight functionality in the extension code base per Magento standard (moved the observer directory in the module root). +- Refactored the autocomplete 2.0 code to make it more developer-friendly to allow for customization per customer needs. +- Collated all autocomplete-specific logic in a single autocomplete.js file and segregated JS-based templates that control the layout of the different autocomplete sources to be more developer-friendly. This enables the customers to easily override the layout of the autocomplete menu in the custom theme and the extension. + + +### FIXES +- Click event in autocomplete +- Autocomplete errors if the product is not assigned a category and indexed into Algolia +- Issues with the price attribute in autocomplete when price attribute is set to Non-Retrievable +- The autocomplete in Query merchandiser (in the Magento admin) shows products from the default store on switching stores [Fixed] +- Issues with triggering Add to Cart Conversion for Configurable Product +- Issues with indexer not updating when product goes out of stock when the last of the inventory is done + + ## 3.8.1 ### UPDATES diff --git a/Helper/ConfigHelper.php b/Helper/ConfigHelper.php index 58e8871db..0817ff877 100755 --- a/Helper/ConfigHelper.php +++ b/Helper/ConfigHelper.php @@ -109,12 +109,23 @@ class ConfigHelper public const DEFAULT_MAX_RECORD_SIZE = 10000; - public const IS_RECOMMEND_FREQUENTLY_BOUGHT_TOGETHER_ENABLED = 'algoliasearch_recommend/recommend/is_frequently_bought_together_enabled'; - public const IS_RECOMMEND_RELATED_PRODUCTS_ENABLED = 'algoliasearch_recommend/recommend/is_related_products_enabled'; - public const NUM_OF_RECOMMEND_FREQUENTLY_BOUGHT_TOGETHER_PRODUCTS = 'algoliasearch_recommend/recommend/num_of_frequently_bought_together_products'; - public const NUM_OF_RECOMMEND_RELATED_PRODUCTS = 'algoliasearch_recommend/recommend/num_of_related_products'; - public const IS_REMOVE_RELATED_PRODUCTS_BLOCK = 'algoliasearch_recommend/recommend/is_remove_core_related_products_block'; - public const IS_REMOVE_UPSELL_PRODUCTS_BLOCK = 'algoliasearch_recommend/recommend/is_remove_core_upsell_products_block'; + 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'; + protected const IS_RECOMMEND_RELATED_PRODUCTS_ENABLED_ON_CART_PAGE = 'algoliasearch_recommend/recommend/related_product/is_related_products_enabled_in_cart_page'; + protected const NUM_OF_RECOMMEND_FREQUENTLY_BOUGHT_TOGETHER_PRODUCTS = 'algoliasearch_recommend/recommend/frequently_bought_together/num_of_frequently_bought_together_products'; + protected const NUM_OF_RECOMMEND_RELATED_PRODUCTS = 'algoliasearch_recommend/recommend/related_product/num_of_related_products'; + protected const IS_REMOVE_RELATED_PRODUCTS_BLOCK = 'algoliasearch_recommend/recommend/related_product/is_remove_core_related_products_block'; + protected const IS_REMOVE_UPSELL_PRODUCTS_BLOCK = 'algoliasearch_recommend/recommend/frequently_bought_together/is_remove_core_upsell_products_block'; + protected const IS_RECOMMEND_TRENDING_ITEMS_ENABLED = 'algoliasearch_recommend/recommend/trends_item/is_trending_items_enabled'; + protected const NUM_OF_TRENDING_ITEMS = 'algoliasearch_recommend/recommend/trends_item/num_of_trending_items'; + protected const TREND_ITEMS_FACET_NAME = 'algoliasearch_recommend/recommend/trends_item/facet_name'; + protected const TREND_ITEMS_FACET_VALUE = 'algoliasearch_recommend/recommend/trends_item/facet_value'; + 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; @@ -452,6 +463,26 @@ public function isRecommendRelatedProductsEnabled($storeId = null) return $this->configInterface->isSetFlag(self::IS_RECOMMEND_RELATED_PRODUCTS_ENABLED, ScopeInterface::SCOPE_STORE, $storeId); } + /** + * @param int $storeId + * + * @return int + */ + public function isRecommendFrequentlyBroughtTogetherEnabledOnCartPage($storeId = null) + { + return $this->configInterface->isSetFlag(self::IS_RECOMMEND_FREQUENTLY_BOUGHT_TOGETHER_ENABLED_ON_CART_PAGE, ScopeInterface::SCOPE_STORE, $storeId); + } + + /** + * @param int $storeId + * + * @return int + */ + public function isRecommendRelatedProductsEnabledOnCartPage($storeId = null) + { + return $this->configInterface->isSetFlag(self::IS_RECOMMEND_RELATED_PRODUCTS_ENABLED_ON_CART_PAGE, ScopeInterface::SCOPE_STORE, $storeId); + } + /** * @param int $storeId * @@ -499,6 +530,121 @@ public function getNumberOfFrequentlyBoughtTogetherProducts($storeId = null) $storeId ); } + + /** + * @param int $storeId + * + * @return int + */ + public function isRecommendTrendingItemsEnabled($storeId = null) + { + return (int) $this->configInterface->getValue( + self::IS_RECOMMEND_TRENDING_ITEMS_ENABLED, + ScopeInterface::SCOPE_STORE, + $storeId + ); + } + + /** + * @param int $storeId + * + * @return int + */ + public function getNumberOfTrendingItems($storeId = null) + { + return (int) $this->configInterface->getValue( + self::NUM_OF_TRENDING_ITEMS, + ScopeInterface::SCOPE_STORE, + $storeId + ); + } + + + /** + * @param int $storeId + * + * @return string + */ + public function getTrendingItemsFacetName($storeId = null) + { + return $this->configInterface->getValue( + self::TREND_ITEMS_FACET_NAME, + ScopeInterface::SCOPE_STORE, + $storeId + ); + } + + /** + * @param int $storeId + * + * @return string + */ + public function getTrendingItemsFacetValue($storeId = null) + { + return $this->configInterface->getValue( + self::TREND_ITEMS_FACET_VALUE, + ScopeInterface::SCOPE_STORE, + $storeId + ); + } + + /** + * @param int $storeId + * + * @return int + */ + public function isTrendItemsEnabledInPDP($storeId = null) + { + return (int) $this->configInterface->getValue( + self::IS_TREND_ITEMS_ENABLED_IN_PDP, + ScopeInterface::SCOPE_STORE, + $storeId + ); + } + + /** + * @param int $storeId + * + * @return int + */ + public function isTrendItemsEnabledInShoppingCart($storeId = null) + { + return (int) $this->configInterface->getValue( + self::IS_TREND_ITEMS_ENABLED_IN_SHOPPING_CART, + ScopeInterface::SCOPE_STORE, + $storeId + ); + } + + /** + * @param int $storeId + * + * @return int + */ + public function isAddToCartEnabledInFrequentlyBoughtTogether($storeId = null) + { + return $this->configInterface->isSetFlag(self::IS_ADDTOCART_ENABLED_IN_FREQUENTLY_BOUGHT_TOGETHER, ScopeInterface::SCOPE_STORE, $storeId); + } + + /** + * @param int $storeId + * + * @return int + */ + public function isAddToCartEnabledInRelatedProducts($storeId = null) + { + return $this->configInterface->isSetFlag(self::IS_ADDTOCART_ENABLED_IN_RELATED_PRODUCTS, ScopeInterface::SCOPE_STORE, $storeId); + } + + /** + * @param int $storeId + * + * @return int + */ + public function isAddToCartEnabledInTrendsItem($storeId = null) + { + return $this->configInterface->isSetFlag(self::IS_ADDTOCART_ENABLED_IN_TRENDS_ITEM, ScopeInterface::SCOPE_STORE, $storeId); + } public function useAdaptiveImage($storeId = null) { diff --git a/Helper/Configuration/AssetHelper.php b/Helper/Configuration/AssetHelper.php index 15528de2e..24bf60010 100644 --- a/Helper/Configuration/AssetHelper.php +++ b/Helper/Configuration/AssetHelper.php @@ -71,10 +71,15 @@ class AssetHelper extends \Magento\Framework\App\Helper\AbstractHelper 'icon' => 'iconFaq', ], [ - 'title' => 'Issues', - 'url' => 'https://github.com/algolia/algoliasearch-magento-2/issues/', + 'title' => 'Support', + 'url' => 'https://www.algolia.com/support/?contact=', 'icon' => 'iconIssues', ], + [ + 'title' => 'Release Notes', + 'url' => 'https://github.com/algolia/algoliasearch-magento-2/releases', + 'icon' => 'iconDocs', + ] ], 'algoliasearch_autocomplete' => [ [ diff --git a/Helper/Configuration/NoticeHelper.php b/Helper/Configuration/NoticeHelper.php index 6d06839d4..0738e8439 100644 --- a/Helper/Configuration/NoticeHelper.php +++ b/Helper/Configuration/NoticeHelper.php @@ -43,6 +43,7 @@ class NoticeHelper extends \Magento\Framework\App\Helper\AbstractHelper 'getVersionNotice', 'getClickAnalyticsNotice', 'getPersonalizationNotice', + 'getRecommendNotice', ]; /** @var array[] */ @@ -317,4 +318,26 @@ public function getNewVersionNotification() { return $this->extensionNotification->checkVersion(); } + + /** + * Function created for adding the Algolia Dashboard link in the Magento recommend system configuration + * @return void + */ + protected function getRecommendNotice() + { + if (!$this->configHelper->getApplicationID()) { + return; + } + $noticeContent = '
'; + + $selector = '#algoliasearch_recommend_recommend'; + $method = 'after'; + + $this->notices[] = [ + 'selector' => $selector, + 'method' => $method, + 'message' => $noticeContent, + ]; + } } diff --git a/Helper/Configuration/PersonalizationHelper.php b/Helper/Configuration/PersonalizationHelper.php index 7088c8dd5..3112088b0 100644 --- a/Helper/Configuration/PersonalizationHelper.php +++ b/Helper/Configuration/PersonalizationHelper.php @@ -34,10 +34,15 @@ class PersonalizationHelper extends \Magento\Framework\App\Helper\AbstractHelper /** @var ConfigResourceInterface */ private $configResourceInterface; + /** + * @param \Magento\Framework\App\Helper\Context $context + * @param ScopeConfigInterface $configInterface + * @param ConfigResourceInterface $configResourceInterface + */ public function __construct( \Magento\Framework\App\Helper\Context $context, - ScopeConfigInterface $configInterface, - ConfigResourceInterface $configResourceInterface + ScopeConfigInterface $configInterface, + ConfigResourceInterface $configResourceInterface ) { $this->configInterface = $configInterface; $this->configResourceInterface = $configResourceInterface; diff --git a/Model/Observer.php b/Model/Observer.php index ec1c9bff5..2d9a8a262 100755 --- a/Model/Observer.php +++ b/Model/Observer.php @@ -14,12 +14,19 @@ */ class Observer implements ObserverInterface { - private $config; - private $registry; - private $storeManager; - private $pageConfig; - private $request; + protected $config; + protected $registry; + protected $storeManager; + protected $pageConfig; + protected $request; + /** + * @param ConfigHelper $configHelper + * @param Registry $registry + * @param StoreManagerInterface $storeManager + * @param PageConfig $pageConfig + * @param \Magento\Framework\App\Request\Http $http + */ public function __construct( ConfigHelper $configHelper, Registry $registry, diff --git a/Model/Observer/Insights/CheckoutCartProductAddAfter.php b/Model/Observer/Insights/CheckoutCartProductAddAfter.php deleted file mode 100644 index 227ae13fb..000000000 --- a/Model/Observer/Insights/CheckoutCartProductAddAfter.php +++ /dev/null @@ -1,90 +0,0 @@ -dataHelper = $dataHelper; - $this->insightsHelper = $insightsHelper; - $this->logger = $logger; - } - - /** - * @return \Algolia\AlgoliaSearch\Helper\ConfigHelper - */ - public function getConfigHelper() - { - return $this->insightsHelper->getConfigHelper(); - } - - /** - * @param Observer $observer - * ['quote_item' => $result, 'product' => $product] - */ - public function execute(Observer $observer) - { - /** @var \Magento\Quote\Model\Quote\Item $quoteItem */ - $quoteItem = $observer->getEvent()->getQuoteItem(); - /** @var \Magento\Catalog\Model\Product $product */ - $product = $observer->getEvent()->getProduct(); - $storeId = $quoteItem->getStoreId(); - - if ($this->getConfigHelper()->isClickConversionAnalyticsEnabled($storeId) - && $this->getConfigHelper()->getConversionAnalyticsMode($storeId) === 'place_order' - && $product->hasData('queryId')) { - $quoteItem->setData('algoliasearch_query_param', $product->getData('queryId')); - } - - if (!$this->insightsHelper->isAddedToCartTracked($storeId)) { - return; - } - - $userClient = $this->insightsHelper->getUserInsightsClient(); - - if ($this->getConfigHelper()->isClickConversionAnalyticsEnabled($storeId) - && $this->getConfigHelper()->getConversionAnalyticsMode($storeId) === 'add_to_cart') { - if ($product->hasData('queryId')) { - try { - $userClient->convertedObjectIDsAfterSearch( - __('Added to Cart'), - $this->dataHelper->getIndexName('_products', $storeId), - [$product->getId()], - $product->getData('queryId') - ); - } catch (\Exception $e) { - $this->logger->critical($e); - } - } - } else { - try { - $userClient->convertedObjectIDs( - __('Added to Cart'), - $this->dataHelper->getIndexName('_products', $storeId), - [$product->getId()] - ); - } catch (\Exception $e) { - $this->logger->critical($e); - } - } - } -} diff --git a/Model/Observer/Insights/CheckoutCartProductAddBefore.php b/Model/Observer/Insights/CheckoutCartProductAddBefore.php deleted file mode 100644 index ebce030d0..000000000 --- a/Model/Observer/Insights/CheckoutCartProductAddBefore.php +++ /dev/null @@ -1,44 +0,0 @@ -configHelper = $configHelper; - $this->insightsHelper = $insightsHelper; - } - - /** - * @param Observer $observer - * ['info' => $requestInfo, 'product' => $product] - */ - public function execute(Observer $observer) - { - /** @var \Magento\Catalog\Model\Product $product */ - $product = $observer->getEvent()->getProduct(); - $requestInfo = $observer->getEvent()->getInfo(); - - if (isset($requestInfo['queryID']) && $requestInfo['queryID'] != '') { - $product->setData('queryId', $requestInfo['queryID']); - } - } -} diff --git a/Observer/Insights/CheckoutCartProductAddAfter.php b/Observer/Insights/CheckoutCartProductAddAfter.php new file mode 100644 index 000000000..364244752 --- /dev/null +++ b/Observer/Insights/CheckoutCartProductAddAfter.php @@ -0,0 +1,108 @@ +dataHelper = $dataHelper; + $this->insightsHelper = $insightsHelper; + $this->logger = $logger; + $this->coreSession = $coreSession; + $this->configHelper = $this->insightsHelper->getConfigHelper(); + $this->personalizationHelper = $this->insightsHelper->getPersonalizationHelper(); + } + + /** + * @param Observer $observer + * ['quote_item' => $result, 'product' => $product] + */ + public function execute(Observer $observer) + { + /** @var Item $quoteItem */ + $quoteItem = $observer->getEvent()->getQuoteItem(); + /** @var Product $product */ + $product = $observer->getEvent()->getProduct(); + $storeId = $quoteItem->getStoreId(); + + if (!$this->insightsHelper->isAddedToCartTracked($storeId) && !$this->insightsHelper->isOrderPlacedTracked($storeId)) { + return; + } + + $userClient = $this->insightsHelper->getUserInsightsClient(); + $queryId = $this->coreSession->getQueryId(); + if ($this->configHelper->isClickConversionAnalyticsEnabled($storeId) && $queryId) { + $conversionAnalyticsMode = $this->configHelper->getConversionAnalyticsMode($storeId); + switch ($conversionAnalyticsMode) { + case 'place_order': + $quoteItem->setData('algoliasearch_query_param', $queryId); + break; + case 'add_to_cart': + try { + $userClient->convertedObjectIDsAfterSearch( + __('Added to Cart'), + $this->dataHelper->getIndexName('_products', $storeId), + [$product->getId()], + $queryId + ); + } catch (Exception $e) { + $this->logger->critical($e); + } + } + } + + if ($this->personalizationHelper->isPersoEnabled($storeId) && $this->personalizationHelper->isCartAddTracked($storeId) && (!$this->configHelper->isClickConversionAnalyticsEnabled($storeId) || $this->configHelper->getConversionAnalyticsMode($storeId) != 'add_to_cart')) { + try { + $userClient->convertedObjectIDs( + __('Added to Cart'), + $this->dataHelper->getIndexName('_products', $storeId), + [$product->getId()] + ); + } catch (Exception $e) { + $this->logger->critical($e); + } + } + } +} \ No newline at end of file diff --git a/Observer/Insights/CheckoutCartProductAddBefore.php b/Observer/Insights/CheckoutCartProductAddBefore.php new file mode 100644 index 000000000..b9034b430 --- /dev/null +++ b/Observer/Insights/CheckoutCartProductAddBefore.php @@ -0,0 +1,35 @@ +coreSession = $coreSession; + } + + /** + * @param Observer $observer + * ['info' => $requestInfo, 'product' => $product] + */ + public function execute(Observer $observer) + { + $requestInfo = $observer->getEvent()->getInfo(); + + if (isset($requestInfo['queryID']) && $requestInfo['queryID'] != '') { + $this->coreSession->setQueryId($requestInfo['queryID']); + } + } +} diff --git a/Model/Observer/Insights/CheckoutOnepageControllerSuccessAction.php b/Observer/Insights/CheckoutOnepageControllerSuccessAction.php similarity index 75% rename from Model/Observer/Insights/CheckoutOnepageControllerSuccessAction.php rename to Observer/Insights/CheckoutOnepageControllerSuccessAction.php index b416a5fd6..c688fed95 100644 --- a/Model/Observer/Insights/CheckoutOnepageControllerSuccessAction.php +++ b/Observer/Insights/CheckoutOnepageControllerSuccessAction.php @@ -1,27 +1,34 @@ insightsHelper = $insightsHelper; $this->orderFactory = $orderFactory; $this->logger = $logger; - } - - /** - * @return \Algolia\AlgoliaSearch\Helper\ConfigHelper - */ - public function getConfigHelper() - { - return $this->insightsHelper->getConfigHelper(); + $this->configHelper = $this->insightsHelper->getConfigHelper(); } /** * @param Observer $observer * - * @return $this|void + * @return $this */ public function execute(Observer $observer) { - /** @var \Magento\Sales\Model\Order $order */ + /** @var Order $order */ $order = $observer->getEvent()->getOrder(); if (!$order) { @@ -71,10 +71,10 @@ public function execute(Observer $observer) $userClient = $this->insightsHelper->getUserInsightsClient(); $orderItems = $order->getAllVisibleItems(); - if ($this->getConfigHelper()->isClickConversionAnalyticsEnabled($order->getStoreId()) - && $this->getConfigHelper()->getConversionAnalyticsMode($order->getStoreId()) === 'place_order') { + if ($this->configHelper->isClickConversionAnalyticsEnabled($order->getStoreId()) + && $this->configHelper->getConversionAnalyticsMode($order->getStoreId()) === 'place_order') { $queryIds = []; - /** @var \Magento\Sales\Model\Order\Item $item */ + /** @var Item $item */ foreach ($orderItems as $item) { if ($item->hasData('algoliasearch_query_param')) { $queryId = $item->getData('algoliasearch_query_param'); @@ -92,17 +92,18 @@ public function execute(Observer $observer) $userClient->convertedObjectIDsAfterSearch( __('Placed Order'), $this->dataHelper->getIndexName('_products', $order->getStoreId()), - $productIds, + array_unique($productIds), $queryId ); - } catch (\Exception $e) { + } catch (Exception $e) { + $this->logger->critical($e); continue; // skip item } } } } else { $productIds = []; - /** @var \Magento\Sales\Model\Order\Item $item */ + /** @var Item $item */ foreach ($orderItems as $item) { $productIds[] = $item->getProductId(); @@ -116,9 +117,9 @@ public function execute(Observer $observer) $userClient->convertedObjectIDs( __('Placed Order'), $this->dataHelper->getIndexName('_products', $order->getStoreId()), - $productIds + array_unique($productIds) ); - } catch (\Exception $e) { + } catch (Exception $e) { $this->logger->critical($e); } } diff --git a/Model/Observer/Insights/CustomerLogin.php b/Observer/Insights/CustomerLogin.php similarity index 68% rename from Model/Observer/Insights/CustomerLogin.php rename to Observer/Insights/CustomerLogin.php index 817ecee22..81e65e6e9 100644 --- a/Model/Observer/Insights/CustomerLogin.php +++ b/Observer/Insights/CustomerLogin.php @@ -1,29 +1,24 @@ configHelper = $configHelper; $this->insightsHelper = $insightsHelper; } @@ -33,7 +28,7 @@ public function __construct( */ public function execute(Observer $observer) { - /** @var \Magento\Customer\Model\Customer $customer */ + /** @var Customer $customer */ $customer = $observer->getEvent()->getCustomer(); if ($this->insightsHelper->getPersonalizationHelper()->isPersoEnabled($customer->getStoreId())) { diff --git a/Model/Observer/Insights/WishlistProductAddAfter.php b/Observer/Insights/WishlistProductAddAfter.php similarity index 62% rename from Model/Observer/Insights/WishlistProductAddAfter.php rename to Observer/Insights/WishlistProductAddAfter.php index 7133e9e5e..1dc53ba62 100644 --- a/Model/Observer/Insights/WishlistProductAddAfter.php +++ b/Observer/Insights/WishlistProductAddAfter.php @@ -1,23 +1,30 @@ dataHelper = $dataHelper; $this->personalisationHelper = $personalisationHelper; $this->insightsHelper = $insightsHelper; + $this->logger = $logger; } /** @@ -42,9 +52,9 @@ public function __construct( */ public function execute(Observer $observer) { - /** @var \Magento\Sales\Model\Order $order */ + /** @var Order $order */ $items = $observer->getEvent()->getItems(); - /** @var \Magento\Wishlist\Model\Item $firstItem */ + /** @var Item $firstItem */ $firstItem = $items[0]; if (!$this->personalisationHelper->isPersoEnabled($firstItem->getStoreId()) @@ -55,15 +65,19 @@ public function execute(Observer $observer) $userClient = $this->insightsHelper->getUserInsightsClient(); $productIds = []; - /** @var \Magento\Wishlist\Model\Item $item */ + /** @var Item $item */ foreach ($items as $item) { $productIds[] = $item->getProductId(); } - $userClient->convertedObjectIDs( - __('Added to Wishlist'), - $this->dataHelper->getIndexName('_products', $firstItem->getStoreId()), - $productIds - ); + try { + $userClient->convertedObjectIDs( + __('Added to Wishlist'), + $this->dataHelper->getIndexName('_products', $firstItem->getStoreId()), + $productIds + ); + } catch (Exception $e) { + $this->logger->critical($e); + } } } diff --git a/Observer/ReindexProductOnLastItemPurchase.php b/Observer/ReindexProductOnLastItemPurchase.php new file mode 100644 index 000000000..9de3c6333 --- /dev/null +++ b/Observer/ReindexProductOnLastItemPurchase.php @@ -0,0 +1,90 @@ +objectManager = $objectManager; + $this->moduleManager = $moduleManager; + $this->productRepository = $productRepository; + $this->indexer = $indexerRegistry->get('algolia_products'); + } + + /** + * @param EventObserver $observer + * @return void + */ + public function execute(EventObserver $observer) + { + /** @var \Magento\Sales\Model\Order\Shipment $shipment */ + $shipment = $observer->getEvent()->getShipment(); + if ($shipment->getOrigData('entity_id')) { + return; + } + + // Add product to Algolia Indexing Queue if last item was purchased and check if Magento MSI related modules are enabled. + if ($this->moduleManager->isEnabled('Magento_Inventory')) { + $isSingleMode = $this->objectManager->create(\Magento\InventoryCatalogApi\Model\IsSingleSourceModeInterface::class); + $defaultSourceProvider = $this->objectManager->create(\Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface::class); + + if (!empty($shipment->getExtensionAttributes()) + && !empty($shipment->getExtensionAttributes()->getSourceCode())) { + $sourceCode = $shipment->getExtensionAttributes()->getSourceCode(); + } elseif ($isSingleMode->execute()) { + $sourceCode = $defaultSourceProvider->getCode(); + } + + foreach ($shipment->getAllItems() as $item) { + $getSourceItemBySku = $this->objectManager->create(\Magento\InventoryApi\Api\GetSourceItemsBySkuInterface::class); + $sourceItemList = $getSourceItemBySku->execute($item->getSku()); + foreach ($sourceItemList as $source) { + if ($source->getSourceCode() == $sourceCode) { + if ($source->getQuantity() < 1) { + $this->indexer->reindexRow($item->getProductId()); + } + } + } + } + } + } +} diff --git a/Observer/ReindexProductOnLastItemPurchaseIfMsiDisable.php b/Observer/ReindexProductOnLastItemPurchaseIfMsiDisable.php new file mode 100644 index 000000000..0bc528e9b --- /dev/null +++ b/Observer/ReindexProductOnLastItemPurchaseIfMsiDisable.php @@ -0,0 +1,73 @@ +moduleManager = $moduleManager; + $this->productRepository = $productRepository; + $this->indexer = $indexerRegistry->get('algolia_products'); + } + + /** + * @param Observer $observer + * @return void + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function execute(Observer $observer) + { + // Add product to Algolia Indexing Queue if last item was purchased and if Magento MSI related modules are disabled. + if (!$this->moduleManager->isEnabled('Magento_Inventory')) { + $quote = $observer->getEvent()->getQuote(); + $productIds = []; + foreach ($quote->getAllItems() as $item) { + $productIds[$item->getProductId()] = $item->getProductId(); + $children = $item->getChildrenItems(); + if ($children) { + foreach ($children as $childItem) { + $productIds[$childItem->getProductId()] = $childItem->getProductId(); + } + } + } + + if ($productIds) { + $productToReindex = []; + foreach ($productIds as $productId) { + $product = $this->productRepository->getById($productId); + $stockInfo = $product->getData('quantity_and_stock_status'); + if ($stockInfo['qty'] < 1) { + $productToReindex[] = $productId; + } + } + $this->indexer->reindexList($productToReindex); + } + } + } +} diff --git a/Plugin/RemovePdpProductsBlock.php b/Plugin/RemovePdpProductsBlock.php index e765c6171..22b3ccb90 100644 --- a/Plugin/RemovePdpProductsBlock.php +++ b/Plugin/RemovePdpProductsBlock.php @@ -30,7 +30,7 @@ public function __construct(ConfigHelper $configHelper) */ public function afterToHtml(AbstractBlock $subject, $result) { - if (($subject->getNameInLayout() === self::RELATED_BLOCK_NAME && $this->_configHelper->isRemoveCoreRelatedProductsBlock()) || ($subject->getNameInLayout() === self::UPSELL_BLOCK_NAME && $this->_configHelper->isRemoveUpsellProductsBlock())) { + if (($subject->getNameInLayout() === self::RELATED_BLOCK_NAME && $this->_configHelper->isRecommendRelatedProductsEnabled() && $this->_configHelper->isRemoveCoreRelatedProductsBlock()) || ($subject->getNameInLayout() === self::UPSELL_BLOCK_NAME && $this->_configHelper->isRecommendFrequentlyBroughtTogetherEnabled() && $this->_configHelper->isRemoveUpsellProductsBlock())) { return ''; } diff --git a/README.md b/README.md index 5b68fd158..cf0a8fcf7 100755 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ Algolia Search for Magento 2 ================== -![Latest version](https://img.shields.io/badge/latest-3.8.1-green) +![Latest version](https://img.shields.io/badge/latest-3.9.0-green) ![Magento 2](https://img.shields.io/badge/Magento-2.4.x-orange) -![PHP](https://img.shields.io/badge/PHP-8.1-blue) +![PHP](https://img.shields.io/badge/PHP-8.1,7.4-blue) [![CircleCI](https://circleci.com/gh/algolia/algoliasearch-magento-2/tree/master.svg?style=svg)](https://circleci.com/gh/algolia/algoliasearch-magento-2/tree/master) @@ -119,7 +119,7 @@ Depending on the extension version you are using, you could have a different PHP | v1.x | [1.28.0](https://github.com/algolia/algoliasearch-client-php/tree/1.28.0) | | v2.x | [2.5.1](https://github.com/algolia/algoliasearch-client-php/tree/2.5.1) | | v3.x | [2.5.1](https://github.com/algolia/algoliasearch-client-php/tree/2.5.1) | -| v3.6.x | [3.2.0](https://github.com/algolia/algoliasearch-client-php/tree/3.2.0) | +| >= v3.6.x | [3.2.0](https://github.com/algolia/algoliasearch-client-php/tree/3.2.0) | Refer to these docs when customising your Algolia Magento extension backend: - [Indexing](https://www.algolia.com/doc/integration/magento-2/how-it-works/indexing/) diff --git a/Setup/Patch/Schema/RecommendConfigPatch.php b/Setup/Patch/Schema/RecommendConfigPatch.php new file mode 100644 index 000000000..aa364b30f --- /dev/null +++ b/Setup/Patch/Schema/RecommendConfigPatch.php @@ -0,0 +1,75 @@ +config = $config; + $this->moduleDataSetup = $moduleDataSetup; + } + + /** + * @return array|string[] + */ + public static function getDependencies() + { + return []; + } + + /** + * @return array|string[] + */ + public function getAliases() + { + return []; + } + + /** + * @return RecommendConfigPatch|void + */ + public function apply() + { + $movedConfigDirectives = [ + 'algoliasearch_recommend/recommend/is_frequently_bought_together_enabled' => 'algoliasearch_recommend/recommend/frequently_bought_together/is_frequently_bought_together_enabled', + 'algoliasearch_recommend/recommend/is_related_products_enabled' => 'algoliasearch_recommend/recommend/related_product/is_related_products_enabled', + 'algoliasearch_recommend/recommend/num_of_frequently_bought_together_products' => 'algoliasearch_recommend/recommend/frequently_bought_together/num_of_frequently_bought_together_products', + 'algoliasearch_recommend/recommend/num_of_related_products' => 'algoliasearch_recommend/recommend/related_product/num_of_related_products', + 'algoliasearch_recommend/recommend/is_remove_core_related_products_block' => 'algoliasearch_recommend/recommend/related_product/is_remove_core_related_products_block', + 'algoliasearch_recommend/recommend/is_remove_core_upsell_products_block' => 'algoliasearch_recommend/recommend/frequently_bought_together/is_remove_core_upsell_products_block' + ]; + + $this->moduleDataSetup->getConnection()->startSetup(); + $connection = $this->moduleDataSetup->getConnection(); + $table = $connection->getTableName('core_config_data'); + foreach ($movedConfigDirectives as $from => $to) { + try { + $connection->query('UPDATE ' . $table . ' SET path = "' . $to . '" WHERE path = "' . $from . '"'); + } catch (\Magento\Framework\DB\Adapter\DuplicateException $e) { + // + } + } + $this->moduleDataSetup->getConnection()->endSetup(); + } +} diff --git a/composer.json b/composer.json index ab0888f17..bed1b8c62 100644 --- a/composer.json +++ b/composer.json @@ -3,10 +3,10 @@ "description": "Algolia Search integration for Magento 2", "type": "magento2-module", "license": ["MIT"], - "version": "3.8.1", + "version": "3.9.0", "require": { "magento/framework": "~102.0|~103.0", - "algolia/algoliasearch-client-php": "^3.2", + "algolia/algoliasearch-client-php": "3.2", "guzzlehttp/guzzle": "^6.3.3|^7.3.0", "ext-json": "*", "ext-PDO": "*", diff --git a/etc/adminhtml/events.xml b/etc/adminhtml/events.xml index 57336ec8d..62132a089 100755 --- a/etc/adminhtml/events.xml +++ b/etc/adminhtml/events.xml @@ -62,4 +62,8 @@