-
Notifications
You must be signed in to change notification settings - Fork 165
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1616 from algolia/release/3.14.2
Release/3.14.2
- Loading branch information
Showing
10 changed files
with
354 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
<?php | ||
|
||
namespace Algolia\AlgoliaSearch\Api; | ||
|
||
interface RecommendManagementInterface | ||
{ | ||
/** | ||
* @param string $productId | ||
* @return array | ||
*/ | ||
public function getBoughtTogetherRecommendation(string $productId): array; | ||
|
||
/** | ||
* @param string $productId | ||
* @return array | ||
*/ | ||
public function getRelatedProductsRecommendation(string $productId): array; | ||
|
||
/** | ||
* @return array | ||
*/ | ||
public function getTrendingItemsRecommendation(): array; | ||
|
||
/** | ||
* @param string $productId | ||
* @return array | ||
*/ | ||
public function getLookingSimilarRecommendation(string $productId): array; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,10 @@ | ||
# CHANGE LOG | ||
|
||
## 3.14.2 | ||
|
||
### Updates | ||
- Trained model check before enabling recommend | ||
|
||
## 3.14.1 | ||
|
||
### Updates | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
<?php | ||
declare(strict_types=1); | ||
|
||
namespace Algolia\AlgoliaSearch\Model; | ||
|
||
use Algolia\AlgoliaSearch\Api\RecommendClient; | ||
use Algolia\AlgoliaSearch\Api\RecommendManagementInterface; | ||
use Algolia\AlgoliaSearch\Helper\ConfigHelper; | ||
use Algolia\AlgoliaSearch\Service\IndexNameFetcher; | ||
use Magento\Framework\Exception\NoSuchEntityException; | ||
|
||
class RecommendManagement implements RecommendManagementInterface | ||
{ | ||
/** | ||
* @var null|RecommendClient | ||
*/ | ||
protected ?RecommendClient $client = null; | ||
|
||
/** | ||
* @param ConfigHelper $configHelper | ||
* @param IndexNameFetcher $indexNameFetcher | ||
*/ | ||
public function __construct( | ||
protected readonly ConfigHelper $configHelper, | ||
protected readonly IndexNameFetcher $indexNameFetcher | ||
){} | ||
|
||
/** | ||
* @return RecommendClient | ||
*/ | ||
protected function getClient(): RecommendClient | ||
{ | ||
if ($this->client === null) { | ||
$this->client = RecommendClient::create( | ||
$this->configHelper->getApplicationID(), | ||
$this->configHelper->getAPIKey() | ||
); | ||
} | ||
return $this->client; | ||
} | ||
|
||
/** | ||
* @param string $productId | ||
* @return array | ||
* @throws NoSuchEntityException | ||
*/ | ||
public function getBoughtTogetherRecommendation(string $productId): array | ||
{ | ||
return $this->getRecommendations($productId, 'bought-together'); | ||
} | ||
|
||
/** | ||
* @param string $productId | ||
* @return array | ||
* @throws NoSuchEntityException | ||
*/ | ||
public function getRelatedProductsRecommendation(string $productId): array | ||
{ | ||
return $this->getRecommendations($productId, 'related-products'); | ||
} | ||
|
||
/** | ||
* @return array | ||
* @throws NoSuchEntityException | ||
*/ | ||
public function getTrendingItemsRecommendation(): array | ||
{ | ||
return $this->getRecommendations('', 'trending-items'); | ||
} | ||
|
||
/** | ||
* @param string $productId | ||
* @return array | ||
* @throws NoSuchEntityException | ||
*/ | ||
public function getLookingSimilarRecommendation(string $productId): array | ||
{ | ||
return $this->getRecommendations($productId, 'bought-together'); | ||
} | ||
|
||
/** | ||
* @param string $productId | ||
* @param string $model | ||
* @param float|int $threshold | ||
* @return array | ||
* @throws NoSuchEntityException | ||
*/ | ||
protected function getRecommendations(string $productId, string $model, float|int $threshold = 50): array | ||
{ | ||
$request['indexName'] = $this->indexNameFetcher->getIndexName('_products'); | ||
$request['model'] = $model; | ||
$request['threshold'] = $threshold; | ||
if (!empty($productId)) { | ||
$request['objectID'] = $productId; | ||
} | ||
|
||
$client = $this->getClient(); | ||
$recommendations = $client->getRecommendations( | ||
[ | ||
'requests' => [ | ||
$request | ||
], | ||
], | ||
); | ||
|
||
return $recommendations['results'][0] ?? []; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
<?php | ||
declare(strict_types=1); | ||
|
||
namespace Algolia\AlgoliaSearch\Observer; | ||
|
||
use Algolia\AlgoliaSearch\Api\RecommendManagementInterface; | ||
use Algolia\AlgoliaSearch\Helper\ConfigHelper; | ||
use Magento\Catalog\Api\ProductRepositoryInterface; | ||
use Magento\Catalog\Model\Product\Visibility; | ||
use Magento\Framework\Api\SearchCriteriaBuilder; | ||
use Magento\Framework\App\Config\Storage\WriterInterface; | ||
use Magento\Framework\Event\Observer; | ||
use Magento\Framework\Event\ObserverInterface; | ||
use Magento\Framework\Exception\LocalizedException; | ||
|
||
class RecommendSettings implements ObserverInterface | ||
{ | ||
const QUANTITY_AND_STOCK_STATUS = 'quantity_and_stock_status'; | ||
const STATUS = 'status'; | ||
const VISIBILITY = 'visibility'; | ||
|
||
/** | ||
* @var string | ||
*/ | ||
protected string $productId = ''; | ||
|
||
/** | ||
* @param ConfigHelper $configHelper | ||
* @param WriterInterface $configWriter | ||
* @param ProductRepositoryInterface $productRepository | ||
* @param RecommendManagementInterface $recommendManagement | ||
* @param SearchCriteriaBuilder $searchCriteriaBuilder | ||
*/ | ||
public function __construct( | ||
protected readonly ConfigHelper $configHelper, | ||
protected readonly WriterInterface $configWriter, | ||
protected readonly ProductRepositoryInterface $productRepository, | ||
protected readonly RecommendManagementInterface $recommendManagement, | ||
protected readonly SearchCriteriaBuilder $searchCriteriaBuilder | ||
){} | ||
|
||
/** | ||
* @param Observer $observer | ||
* @throws LocalizedException | ||
*/ | ||
public function execute(Observer $observer): void | ||
{ | ||
foreach ($observer->getData('changed_paths') as $changedPath) { | ||
// Validate before enable FBT on PDP or on cart page | ||
if (( | ||
$changedPath == ConfigHelper::IS_RECOMMEND_FREQUENTLY_BOUGHT_TOGETHER_ENABLED | ||
&& $this->configHelper->isRecommendFrequentlyBroughtTogetherEnabled() | ||
) || ( | ||
$changedPath == ConfigHelper::IS_RECOMMEND_FREQUENTLY_BOUGHT_TOGETHER_ENABLED_ON_CART_PAGE | ||
&& $this->configHelper->isRecommendFrequentlyBroughtTogetherEnabledOnCartPage() | ||
)) { | ||
$this->validateFrequentlyBroughtTogether($changedPath); | ||
} | ||
|
||
// Validate before enable related products on PDP or on cart page | ||
if (( | ||
$changedPath == ConfigHelper::IS_RECOMMEND_RELATED_PRODUCTS_ENABLED | ||
&& $this->configHelper->isRecommendRelatedProductsEnabled() | ||
) || ( | ||
$changedPath == ConfigHelper::IS_RECOMMEND_RELATED_PRODUCTS_ENABLED_ON_CART_PAGE | ||
&& $this->configHelper->isRecommendRelatedProductsEnabledOnCartPage() | ||
)) { | ||
$this->validateRelatedProducts($changedPath); | ||
} | ||
|
||
// Validate before enable trending items on PDP or on cart page | ||
if (( | ||
$changedPath == ConfigHelper::IS_TREND_ITEMS_ENABLED_IN_PDP | ||
&& $this->configHelper->isTrendItemsEnabledInPDP() | ||
) || ( | ||
$changedPath == ConfigHelper::IS_TREND_ITEMS_ENABLED_IN_SHOPPING_CART | ||
&& $this->configHelper->isTrendItemsEnabledInShoppingCart() | ||
)) { | ||
$this->validateTrendingItems($changedPath); | ||
} | ||
|
||
// Validate before enable looking similar on PDP or on cart page | ||
if (( | ||
$changedPath == ConfigHelper::IS_LOOKING_SIMILAR_ENABLED_IN_PDP | ||
&& $this->configHelper->isLookingSimilarEnabledInPDP() | ||
) || ( | ||
$changedPath == ConfigHelper::IS_LOOKING_SIMILAR_ENABLED_IN_SHOPPING_CART | ||
&& $this->configHelper->isLookingSimilarEnabledInShoppingCart() | ||
)) { | ||
$this->validateLookingSimilar($changedPath); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* @param string $changedPath | ||
* @return void | ||
* @throws LocalizedException | ||
*/ | ||
protected function validateFrequentlyBroughtTogether(string $changedPath): void | ||
{ | ||
$this->validateRecommendation($changedPath, 'getBoughtTogetherRecommendation', 'Frequently Bought Together'); | ||
} | ||
|
||
/** | ||
* @param string $changedPath | ||
* @return void | ||
* @throws LocalizedException | ||
*/ | ||
protected function validateRelatedProducts(string $changedPath): void | ||
{ | ||
$this->validateRecommendation($changedPath, 'getRelatedProductsRecommendation', 'Related Products'); | ||
} | ||
|
||
/** | ||
* @param string $changedPath | ||
* @return void | ||
* @throws LocalizedException | ||
*/ | ||
protected function validateTrendingItems(string $changedPath): void | ||
{ | ||
$this->validateRecommendation($changedPath, 'getTrendingItemsRecommendation', 'Trending Items'); | ||
} | ||
|
||
/** | ||
* @param string $changedPath | ||
* @return void | ||
* @throws LocalizedException | ||
*/ | ||
protected function validateLookingSimilar(string $changedPath): void | ||
{ | ||
$this->validateRecommendation($changedPath, 'getLookingSimilarRecommendation', 'Looking Similar'); | ||
} | ||
|
||
/** | ||
* @param string $changedPath - config path to be reverted if validation failed | ||
* @param string $recommendationMethod - name of method to call to retrieve method from RecommendManagementInterface | ||
* @param string $modelName - user friendly name to refer to model in error messaging | ||
* @return void | ||
* @throws LocalizedException | ||
*/ | ||
protected function validateRecommendation(string $changedPath, string $recommendationMethod, string $modelName): void | ||
{ | ||
try { | ||
$recommendations = $this->recommendManagement->$recommendationMethod($this->getProductId()); | ||
if (empty($recommendations['renderingContent'])) { | ||
throw new LocalizedException(__( | ||
"It appears that there is no trained model available for Algolia application ID %1.", | ||
$this->configHelper->getApplicationID() | ||
)); | ||
} | ||
} catch (\Exception $e) { | ||
$this->configWriter->save($changedPath, 0); | ||
throw new LocalizedException(__( | ||
"Unable to save %1 Recommend configuration due to the following error: %2", | ||
$modelName, | ||
$e->getMessage() | ||
) | ||
); | ||
} | ||
} | ||
|
||
/** | ||
* @return string - Product ID string for use in API calls | ||
* @throws LocalizedException | ||
*/ | ||
protected function getProductId(): string | ||
{ | ||
if ($this->productId === '') { | ||
$searchCriteria = $this->searchCriteriaBuilder | ||
->addFilter(self::STATUS, 1) | ||
->addFilter(self::QUANTITY_AND_STOCK_STATUS, 1) | ||
->addFilter( | ||
self::VISIBILITY, | ||
[ | ||
Visibility::VISIBILITY_IN_CATALOG, | ||
Visibility::VISIBILITY_IN_SEARCH, | ||
Visibility::VISIBILITY_BOTH | ||
], | ||
'in') | ||
->setPageSize(1) | ||
->create(); | ||
$result = $this->productRepository->getList($searchCriteria); | ||
$items = $result->getItems(); | ||
$firstProduct = reset($items); | ||
if ($firstProduct) { | ||
$this->productId = (string) $firstProduct->getId(); | ||
} else { | ||
throw new LocalizedException(__("Unable to locate product to validate Recommend model.")); | ||
} | ||
} | ||
|
||
return $this->productId; | ||
} | ||
} |
Oops, something went wrong.