Skip to content

Commit 423c3cd

Browse files
committed
MAGE-940: validation put in place before enable recommendations
1 parent 8214531 commit 423c3cd

File tree

6 files changed

+394
-10
lines changed

6 files changed

+394
-10
lines changed

Api/RecommendManagementInterface.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
namespace Algolia\AlgoliaSearch\Api;
3+
interface RecommendManagementInterface
4+
{
5+
/**
6+
* @param string $productId
7+
* @return array
8+
*/
9+
public function getBoughtTogetherRecommendation(string $productId): array;
10+
11+
/**
12+
* @param string $productId
13+
* @return array
14+
*/
15+
public function getRelatedProductsRecommendation(string $productId): array;
16+
17+
/**
18+
* @return array
19+
*/
20+
public function getTrendingItemsRecommendation(): array;
21+
22+
/**
23+
* @param string $productId
24+
* @return array
25+
*/
26+
public function getLookingSimilarRecommendation(string $productId): array;
27+
}

Helper/ConfigHelper.php

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -119,30 +119,30 @@ class ConfigHelper
119119
public const EXTRA_SETTINGS_ADDITIONAL_SECTIONS =
120120
'algoliasearch_extra_settings/extra_settings/additional_sections_extra_settings';
121121
public const MAGENTO_DEFAULT_CACHE_TIME = 'system/full_page_cache/ttl';
122-
protected const IS_RECOMMEND_FREQUENTLY_BOUGHT_TOGETHER_ENABLED = 'algoliasearch_recommend/recommend/frequently_bought_together/is_frequently_bought_together_enabled';
123-
protected const IS_RECOMMEND_RELATED_PRODUCTS_ENABLED = 'algoliasearch_recommend/recommend/related_product/is_related_products_enabled';
124-
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';
125-
protected const IS_RECOMMEND_RELATED_PRODUCTS_ENABLED_ON_CART_PAGE = 'algoliasearch_recommend/recommend/related_product/is_related_products_enabled_in_cart_page';
122+
public const IS_RECOMMEND_FREQUENTLY_BOUGHT_TOGETHER_ENABLED = 'algoliasearch_recommend/recommend/frequently_bought_together/is_frequently_bought_together_enabled';
123+
public const IS_RECOMMEND_RELATED_PRODUCTS_ENABLED = 'algoliasearch_recommend/recommend/related_product/is_related_products_enabled';
124+
public const IS_RECOMMEND_FREQUENTLY_BOUGHT_TOGETHER_ENABLED_ON_CART_PAGE = 'algoliasearch_recommend/recommend/frequently_bought_together/is_frequently_bought_together_enabled_in_cart_page';
125+
public const IS_RECOMMEND_RELATED_PRODUCTS_ENABLED_ON_CART_PAGE = 'algoliasearch_recommend/recommend/related_product/is_related_products_enabled_in_cart_page';
126126
protected const NUM_OF_RECOMMEND_FREQUENTLY_BOUGHT_TOGETHER_PRODUCTS = 'algoliasearch_recommend/recommend/frequently_bought_together/num_of_frequently_bought_together_products';
127127
protected const NUM_OF_RECOMMEND_RELATED_PRODUCTS = 'algoliasearch_recommend/recommend/related_product/num_of_related_products';
128128
protected const IS_REMOVE_RELATED_PRODUCTS_BLOCK = 'algoliasearch_recommend/recommend/related_product/is_remove_core_related_products_block';
129129
protected const IS_REMOVE_UPSELL_PRODUCTS_BLOCK = 'algoliasearch_recommend/recommend/frequently_bought_together/is_remove_core_upsell_products_block';
130-
protected const IS_RECOMMEND_TRENDING_ITEMS_ENABLED = 'algoliasearch_recommend/recommend/trends_item/is_trending_items_enabled';
130+
public const IS_RECOMMEND_TRENDING_ITEMS_ENABLED = 'algoliasearch_recommend/recommend/trends_item/is_trending_items_enabled';
131131
protected const IS_RECOMMEND_LOOKING_SIMILAR_ENABLED = 'algoliasearch_recommend/recommend/looking_similar/is_looking_similar_enabled';
132132
protected const NUM_OF_LOOKING_SIMILAR = 'algoliasearch_recommend/recommend/looking_similar/num_of_products';
133133
protected const NUM_OF_TRENDING_ITEMS = 'algoliasearch_recommend/recommend/trends_item/num_of_trending_items';
134134
protected const TREND_ITEMS_FACET_NAME = 'algoliasearch_recommend/recommend/trends_item/facet_name';
135135
protected const TREND_ITEMS_FACET_VALUE = 'algoliasearch_recommend/recommend/trends_item/facet_value';
136-
protected const IS_TREND_ITEMS_ENABLED_IN_PDP = 'algoliasearch_recommend/recommend/trends_item/is_trending_items_enabled_on_pdp';
137-
protected const IS_TREND_ITEMS_ENABLED_IN_SHOPPING_CART = 'algoliasearch_recommend/recommend/trends_item/is_trending_items_enabled_on_cart_page';
136+
public const IS_TREND_ITEMS_ENABLED_IN_PDP = 'algoliasearch_recommend/recommend/trends_item/is_trending_items_enabled_on_pdp';
137+
public const IS_TREND_ITEMS_ENABLED_IN_SHOPPING_CART = 'algoliasearch_recommend/recommend/trends_item/is_trending_items_enabled_on_cart_page';
138138
protected const IS_ADDTOCART_ENABLED_IN_FREQUENTLY_BOUGHT_TOGETHER = 'algoliasearch_recommend/recommend/frequently_bought_together/is_addtocart_enabled';
139139
protected const IS_ADDTOCART_ENABLED_IN_RELATED_PRODUCTS = 'algoliasearch_recommend/recommend/related_product/is_addtocart_enabled';
140140
protected const IS_ADDTOCART_ENABLED_IN_TRENDS_ITEM = 'algoliasearch_recommend/recommend/trends_item/is_addtocart_enabled';
141141
protected const IS_ADDTOCART_ENABLED_IN_LOOKING_SIMILAR = 'algoliasearch_recommend/recommend/looking_similar/is_addtocart_enabled';
142-
protected const IS_LOOKING_SIMILAR_ENABLED_IN_PDP = 'algoliasearch_recommend/recommend/looking_similar/is_looking_similar_enabled_on_pdp';
143-
protected const IS_LOOKING_SIMILAR_ENABLED_IN_SHOPPING_CART = 'algoliasearch_recommend/recommend/looking_similar/is_looking_similar_enabled_on_cart_page';
142+
public const IS_LOOKING_SIMILAR_ENABLED_IN_PDP = 'algoliasearch_recommend/recommend/looking_similar/is_looking_similar_enabled_on_pdp';
143+
public const IS_LOOKING_SIMILAR_ENABLED_IN_SHOPPING_CART = 'algoliasearch_recommend/recommend/looking_similar/is_looking_similar_enabled_on_cart_page';
144144
protected const LOOKING_SIMILAR_TITLE = 'algoliasearch_recommend/recommend/looking_similar/title';
145-
public const LEGACY_USE_VIRTUAL_REPLICA_ENABLED = 'algoliasearch_instant/instant/use_virtual_replica';
145+
public const LEGACY_USE_VIRTUAL_REPLICA_ENABLED = 'algoliasearch_instant/instant/use_virtual_replica';
146146
protected const AUTOCOMPLETE_KEYBORAD_NAVIAGATION = 'algoliasearch_autocomplete/autocomplete/navigator';
147147
protected const FREQUENTLY_BOUGHT_TOGETHER_TITLE = 'algoliasearch_recommend/recommend/frequently_bought_together/title';
148148
protected const RELATED_PRODUCTS_TITLE = 'algoliasearch_recommend/recommend/related_product/title';

Model/Observer/RecommendSettings.php

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Algolia\AlgoliaSearch\Model\Observer;
5+
6+
use Algolia\AlgoliaSearch\Api\RecommendManagementInterface;
7+
use Algolia\AlgoliaSearch\Helper\ConfigHelper;
8+
use Magento\Catalog\Api\ProductRepositoryInterface;
9+
use Magento\Framework\Api\SearchCriteriaBuilder;
10+
use Magento\Framework\App\Config\Storage\WriterInterface;
11+
use Magento\Framework\Event\ObserverInterface;
12+
use Magento\Framework\Event\Observer;
13+
use Magento\Framework\Exception\LocalizedException;
14+
15+
class RecommendSettings implements ObserverInterface
16+
{
17+
/**
18+
* @var string
19+
*/
20+
private $productId = '';
21+
22+
/**
23+
* @param ConfigHelper $configHelper
24+
* @param WriterInterface $configWriter
25+
* @param ProductRepositoryInterface $productRepository
26+
* @param RecommendManagementInterface $recommendManagement
27+
* @param SearchCriteriaBuilder $searchCriteriaBuilder
28+
*/
29+
public function __construct(
30+
private readonly ConfigHelper $configHelper,
31+
private readonly WriterInterface $configWriter,
32+
private readonly ProductRepositoryInterface $productRepository,
33+
private readonly RecommendManagementInterface $recommendManagement,
34+
private readonly SearchCriteriaBuilder $searchCriteriaBuilder
35+
){}
36+
37+
/**
38+
* @param Observer $observer
39+
* @throws LocalizedException
40+
*/
41+
public function execute(Observer $observer)
42+
{
43+
foreach ($observer->getData('changed_paths') as $changedPath) {
44+
// Validate before enable FBT on PDP
45+
if (
46+
$changedPath == $this->configHelper::IS_RECOMMEND_FREQUENTLY_BOUGHT_TOGETHER_ENABLED
47+
&& $this->configHelper->isRecommendFrequentlyBroughtTogetherEnabled()
48+
) {
49+
$this->validateFrequentlyBroughtTogether($changedPath);
50+
}
51+
52+
// Validate before enable FBT on cart page
53+
if (
54+
$changedPath == $this->configHelper::IS_RECOMMEND_FREQUENTLY_BOUGHT_TOGETHER_ENABLED_ON_CART_PAGE
55+
&& $this->configHelper->isRecommendFrequentlyBroughtTogetherEnabledOnCartPage()
56+
) {
57+
$this->validateFrequentlyBroughtTogether($changedPath);
58+
}
59+
60+
// Validate before enable related products on PDP
61+
if (
62+
$changedPath == $this->configHelper::IS_RECOMMEND_RELATED_PRODUCTS_ENABLED
63+
&& $this->configHelper->isRecommendRelatedProductsEnabled()
64+
) {
65+
$this->validateRelatedProducts($changedPath);
66+
}
67+
68+
// Validate before enable related products on cart page
69+
if (
70+
$changedPath == $this->configHelper::IS_RECOMMEND_RELATED_PRODUCTS_ENABLED_ON_CART_PAGE
71+
&& $this->configHelper->isRecommendRelatedProductsEnabledOnCartPage()
72+
) {
73+
$this->validateRelatedProducts($changedPath);
74+
}
75+
76+
// Validate before enable trending items on PDP
77+
if (
78+
$changedPath == $this->configHelper::IS_TREND_ITEMS_ENABLED_IN_PDP
79+
&& $this->configHelper->isTrendItemsEnabledInPDP()
80+
) {
81+
$this->validateTrendingItems($changedPath);
82+
}
83+
84+
// Validate before enable trending items on cart page
85+
if (
86+
$changedPath == $this->configHelper::IS_TREND_ITEMS_ENABLED_IN_SHOPPING_CART
87+
&& $this->configHelper->isTrendItemsEnabledInShoppingCart()
88+
) {
89+
$this->validateTrendingItems($changedPath);
90+
}
91+
92+
// Validate before enable looking similar on PDP
93+
if (
94+
$changedPath == $this->configHelper::IS_LOOKING_SIMILAR_ENABLED_IN_PDP
95+
&& $this->configHelper->isLookingSimilarEnabledInPDP()
96+
) {
97+
$this->validateLookingSimilar($changedPath);
98+
}
99+
100+
// Validate before enable looking similar on cart page
101+
if (
102+
$changedPath == $this->configHelper::IS_LOOKING_SIMILAR_ENABLED_IN_SHOPPING_CART
103+
&& $this->configHelper->isLookingSimilarEnabledInShoppingCart()
104+
) {
105+
$this->validateLookingSimilar($changedPath);
106+
}
107+
}
108+
}
109+
110+
/**
111+
* @param string $changedPath
112+
* @return void
113+
* @throws LocalizedException
114+
*/
115+
protected function validateFrequentlyBroughtTogether(string $changedPath): void
116+
{
117+
try {
118+
$recommendations = $this->recommendManagement->getBoughtTogetherRecommendation($this->getProductId());
119+
if (empty($recommendations['renderingContent'])) {
120+
$this->configWriter->save($changedPath, 0);
121+
throw new LocalizedException(__(
122+
"It appears that there is no trained model available for Frequently Bought Together recommendation for the AppID: %1.",
123+
$this->configHelper->getApplicationID()
124+
));
125+
}
126+
} catch (\Exception $e) {
127+
$this->configWriter->save($changedPath, 0);
128+
throw new LocalizedException(__($e->getMessage()));
129+
}
130+
}
131+
132+
/**
133+
* @param string $changedPath
134+
* @return void
135+
* @throws LocalizedException
136+
*/
137+
protected function validateRelatedProducts(string $changedPath): void
138+
{
139+
try {
140+
$recommendations = $this->recommendManagement->getRelatedProductsRecommendation($this->getProductId());
141+
if (empty($recommendations['renderingContent'])) {
142+
$this->configWriter->save($changedPath, 0);
143+
throw new LocalizedException(__(
144+
"It appears that there is no trained model available for Related Products recommendation for the AppID: %1.",
145+
$this->configHelper->getApplicationID()
146+
));
147+
}
148+
} catch (\Exception $e) {
149+
$this->configWriter->save($changedPath, 0);
150+
throw new LocalizedException(__($e->getMessage()));
151+
}
152+
}
153+
154+
/**
155+
* @param string $changedPath
156+
* @return void
157+
* @throws LocalizedException
158+
*/
159+
protected function validateTrendingItems(string $changedPath): void
160+
{
161+
try {
162+
$recommendations = $this->recommendManagement->getTrendingItemsRecommendation();
163+
// When no recommendations suggested, most likely trained model is missing
164+
if (empty($recommendations['renderingContent'])) {
165+
$this->configWriter->save($changedPath, 0);
166+
throw new LocalizedException(__(
167+
"It appears that there is no trained model available for Trending Items recommendation for the AppID: %1.",
168+
$this->configHelper->getApplicationID()
169+
));
170+
}
171+
} catch (\Exception $e) {
172+
$this->configWriter->save($changedPath, 0);
173+
throw new LocalizedException(__($e->getMessage()));
174+
}
175+
}
176+
177+
/**
178+
* @param string $changedPath
179+
* @return void
180+
* @throws LocalizedException
181+
*/
182+
protected function validateLookingSimilar(string $changedPath): void
183+
{
184+
try {
185+
$recommendations = $this->recommendManagement->getLookingSimilarRecommendation($this->getProductId());
186+
if (empty($recommendations['renderingContent'])) {
187+
$this->configWriter->save($changedPath, 0);
188+
throw new LocalizedException(__(
189+
"It appears that there is no trained model available for Looking Similar Recommendation for the AppID: %1.",
190+
$this->configHelper->getApplicationID()
191+
));
192+
}
193+
} catch (\Exception $e) {
194+
$this->configWriter->save($changedPath, 0);
195+
throw new LocalizedException(__($e->getMessage()));
196+
}
197+
}
198+
199+
/**
200+
* @return string
201+
*/
202+
private function getProductId(): string
203+
{
204+
if ($this->productId === '') {
205+
$searchCriteria = $this->searchCriteriaBuilder
206+
->addFilter('status', 1)
207+
->create();
208+
$result = $this->productRepository->getList($searchCriteria);
209+
$products = array_reverse($result->getItems());
210+
$firstProduct = array_pop($products);
211+
$this->productId = (string)$firstProduct->getId();
212+
}
213+
214+
return $this->productId;
215+
}
216+
}

0 commit comments

Comments
 (0)