Skip to content

Commit

Permalink
Merge pull request #1680 from algolia/release/3.13.7-dev
Browse files Browse the repository at this point in the history
Release/3.13.7 dev
  • Loading branch information
damcou authored Jan 15, 2025
2 parents 515bf3d + c388e0c commit adfa7e8
Show file tree
Hide file tree
Showing 16 changed files with 517 additions and 50 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
# CHANGE LOG

## 3.13.7

### Features
- Added a feature to enable automatic price indexing on the Advanced section of the configuration (This feature should help alleviate issues where missing pricing records prevent Algolia from being able to index products.)

### Updates
- Updated `getCookie` method to make it more consistent
- Removed dependency to `catalog_product_price` indexer

### Bug Fixes
- Fixed a bug where the Landing Page Builder was crashing on save with customer group pricing was enabled.
- Fixed issue where Insights information wasn't kept on the url after clicking "add to cart" on PLP powered by InstantSearch

## 3.13.6

### Bug Fixes
Expand Down
16 changes: 9 additions & 7 deletions Controller/Adminhtml/Landingpage/Save.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,15 @@ public function execute()
$data['configuration'] = $data['algolia_configuration'];
if ($this->configHelper->isCustomerGroupsEnabled($data['store_id'])) {
$configuration = json_decode($data['algolia_configuration'], true);
$priceConfig = $configuration['price'.$data['price_key']];
$customerGroups = $this->customerGroupCollectionFactory->create();
$store = $this->storeManager->getStore($data['store_id']);
$baseCurrencyCode = $store->getBaseCurrencyCode();
foreach ($customerGroups as $group) {
$groupId = (int) $group->getData('customer_group_id');
$configuration['price.'.$baseCurrencyCode.'.group_'.$groupId] = $priceConfig;
if (isset($configuration['price'.$data['price_key']])) {
$priceConfig = $configuration['price' . $data['price_key']];
$customerGroups = $this->customerGroupCollectionFactory->create();
$store = $this->storeManager->getStore($data['store_id']);
$baseCurrencyCode = $store->getBaseCurrencyCode();
foreach ($customerGroups as $group) {
$groupId = (int)$group->getData('customer_group_id');
$configuration['price.' . $baseCurrencyCode . '.group_' . $groupId] = $priceConfig;
}
}
$data['configuration'] = json_encode($configuration);
}
Expand Down
10 changes: 10 additions & 0 deletions Helper/ConfigHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ class ConfigHelper
public const CONNECTION_TIMEOUT = 'algoliasearch_advanced/advanced/connection_timeout';
public const READ_TIMEOUT = 'algoliasearch_advanced/advanced/read_timeout';
public const WRITE_TIMEOUT = 'algoliasearch_advanced/advanced/write_timeout';
public const AUTO_PRICE_INDEXING_ENABLED = 'algoliasearch_advanced/advanced/auto_price_indexing';

public const SHOW_OUT_OF_STOCK = 'cataloginventory/options/show_out_of_stock';

Expand Down Expand Up @@ -1222,6 +1223,15 @@ public function getWriteTimeout($storeId = null)
return $this->configInterface->getValue(self::WRITE_TIMEOUT, ScopeInterface::SCOPE_STORE, $storeId);
}

public function isAutoPriceIndexingEnabled(?int $storeId = null): bool
{
return $this->configInterface->isSetFlag(
self::AUTO_PRICE_INDEXING_ENABLED,
ScopeInterface::SCOPE_STORE,
$storeId
);
}

/**
* @param $storeId
* @return array|bool|float|int|mixed|string
Expand Down
57 changes: 27 additions & 30 deletions Helper/Data.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Algolia\AlgoliaSearch\Helper\Entity\PageHelper;
use Algolia\AlgoliaSearch\Helper\Entity\ProductHelper;
use Algolia\AlgoliaSearch\Helper\Entity\SuggestionHelper;
use Algolia\AlgoliaSearch\Service\Product\MissingPriceIndexHandler;
use Magento\Catalog\Model\Category;
use Magento\Catalog\Model\Product;
use Magento\Catalog\Model\ResourceModel\Product\Collection;
Expand Down Expand Up @@ -76,6 +77,11 @@ class Data
*/
protected $storeManager;

/**
* @var MissingPriceIndexHandler
*/
protected $missingPriceIndexHandler;

protected $emulationRuns = false;

/** @var \Magento\Framework\Indexer\IndexerInterface */
Expand All @@ -98,20 +104,21 @@ class Data
* @param StoreManagerInterface $storeManager
*/
public function __construct(
AlgoliaHelper $algoliaHelper,
ConfigHelper $configHelper,
ProductHelper $producthelper,
CategoryHelper $categoryHelper,
PageHelper $pageHelper,
SuggestionHelper $suggestionHelper,
AdditionalSectionHelper $additionalSectionHelper,
Emulation $emulation,
Logger $logger,
ResourceConnection $resource,
ManagerInterface $eventManager,
ScopeCodeResolver $scopeCodeResolver,
StoreManagerInterface $storeManager,
IndexerRegistry $indexerRegistry
AlgoliaHelper $algoliaHelper,
ConfigHelper $configHelper,
ProductHelper $producthelper,
CategoryHelper $categoryHelper,
PageHelper $pageHelper,
SuggestionHelper $suggestionHelper,
AdditionalSectionHelper $additionalSectionHelper,
Emulation $emulation,
Logger $logger,
ResourceConnection $resource,
ManagerInterface $eventManager,
ScopeCodeResolver $scopeCodeResolver,
StoreManagerInterface $storeManager,
MissingPriceIndexHandler $missingPriceIndexHandler,
IndexerRegistry $indexerRegistry

)
{
Expand All @@ -128,6 +135,7 @@ public function __construct(
$this->eventManager = $eventManager;
$this->scopeCodeResolver = $scopeCodeResolver;
$this->storeManager = $storeManager;
$this->missingPriceIndexHandler = $missingPriceIndexHandler;

$this->priceIndexer = $indexerRegistry->get('catalog_product_price');
}
Expand Down Expand Up @@ -421,8 +429,6 @@ public function rebuildStoreProductIndex($storeId, $productIds)
return;
}

$this->checkPriceIndex($productIds);

$this->startEmulation($storeId);
$this->logger->start('Indexing');
try {
Expand Down Expand Up @@ -753,6 +759,11 @@ public function rebuildStoreProductIndexPage(
'store' => $storeId
]
);

if ($this->configHelper->isAutoPriceIndexingEnabled($storeId)) {
$this->missingPriceIndexHandler->refreshPriceIndex($collection);
}

$logMessage = 'LOADING: ' . $this->logger->getStoreName($storeId) . ',
collection page: ' . $page . ',
pageSize: ' . $pageSize;
Expand Down Expand Up @@ -973,18 +984,4 @@ protected function deleteInactiveIds($storeId, $objectIds, $indexName)
$idsToDeleteFromAlgolia = array_diff($objectIds, $dbIds);
$this->algoliaHelper->deleteObjects($idsToDeleteFromAlgolia, $indexName);
}

/**
* If the price index is stale
* @param array $productIds
* @return void
*/
protected function checkPriceIndex(array $productIds): void
{
$state = $this->priceIndexer->getState()->getStatus();
if ($state === \Magento\Framework\Indexer\StateInterface::STATUS_INVALID) {
$this->priceIndexer->reindexList($productIds);
}
}

}
24 changes: 24 additions & 0 deletions Model/Config/AutomaticPriceIndexingComment.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace Algolia\AlgoliaSearch\Model\Config;

use Magento\Config\Model\Config\CommentInterface;
use Magento\Framework\UrlInterface;

class AutomaticPriceIndexingComment implements CommentInterface
{
public function __construct(
protected UrlInterface $urlInterface
) { }

public function getCommentText($elementValue)
{
$url = $this->urlInterface->getUrl('https://www.algolia.com/doc/integration/magento-2/how-it-works/indexing-queue/#configure-the-queue');

$comment = array();
$comment[] = 'Algolia relies on the core Magento Product Price index when serializing product data. If the price index is not up to date, Algolia will not be able to accurately determine what should be included in the search index.';
$comment[] = 'If you are experiencing problems with products not syncing to Algolia due to this issue, enabling this setting will allow Algolia to automatically update the price index as needed.';
$comment[] = 'NOTE: This can introduce a marginal amount of overhead to the indexing process so only enable if necessary. Be sure to <a href="' . $url . '" target="_blank">optimize the indexing queue</a> based on the impact of this operation.';
return implode('<br><br>', $comment);
}
}
201 changes: 201 additions & 0 deletions Plugin/AddToCartRedirectForInsights.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
<?php

namespace Algolia\AlgoliaSearch\Plugin;

use Algolia\AlgoliaSearch\Helper\ConfigHelper;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Catalog\Model\Product;
use Magento\CatalogInventory\Api\StockRegistryInterface;
use Magento\Checkout\Model\Cart;
use Magento\Checkout\Model\Cart\RequestInfoFilterInterface;
use Magento\Checkout\Model\Session;
use Magento\Framework\DataObject;
use Magento\Framework\Event\ManagerInterface;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Store\Model\StoreManagerInterface;

class AddToCartRedirectForInsights
{
/**
* @var RequestInfoFilterInterface
*/
private $requestInfoFilter;

public function __construct(
protected StoreManagerInterface $storeManager,
protected ProductRepositoryInterface $productRepository,
protected Session $checkoutSession,
protected StockRegistryInterface $stockRegistry,
protected ManagerInterface $eventManager,
protected ConfigHelper $configHelper,
) {}

/**
* @param Cart $cartModel
* @param int|Product $productInfo
* @param array|int|DataObject|null $requestInfo
*
* @return null
*
* @throws LocalizedException
* @throws NoSuchEntityException
*/
public function beforeAddProduct(Cart $cartModel, int|Product $productInfo, array|int|DataObject $requestInfo = null)
{
// First, check is Insights are enabled
if (!$this->configHelper->isClickConversionAnalyticsEnabled($this->storeManager->getStore()->getId())) {
return;
}

// If the request doesn't have any insights info, no need to handle it
if (!isset($requestInfo['referer']) || !isset($requestInfo['queryID']) || !isset($requestInfo['indexName'])) {
return;
}

// Check if the request comes from the PLP handled by InstantSearch
if ($requestInfo['referer'] != 'instantsearch') {
return;
}

$product = $this->getProduct($productInfo);
$productId = $product->getId();

if ($productId) {
$request = $this->getQtyRequest($product, $requestInfo);

try {
$result = $product->getTypeInstance()->prepareForCartAdvanced($request, $product);
} catch (LocalizedException $e) {
$this->checkoutSession->setUseNotice(false);
$result = $e->getMessage();
}

// if the result is a string, this mean that the product can't be added to the cart
// see Magento\Quote\Model\Quote::addProduct()
// Here we need to add the insights information to the redirect
if (is_string($result)) {
$redirectUrl = $product->getUrlModel()->getUrl(
$product,
[
'_query' => [
'objectID' => $product->getId(),
'queryID' => $requestInfo['queryID'],
'indexName' => $requestInfo['indexName']
]
]
);

$this->checkoutSession->setRedirectUrl($redirectUrl);
if ($this->checkoutSession->getUseNotice() === null) {
$this->checkoutSession->setUseNotice(true);
}
throw new LocalizedException(__($result));
}
}
}

/**
* @param $productInfo
*
* @return Product
*
* @throws NoSuchEntityException
* @throws LocalizedException
*/
protected function getProduct($productInfo): Product
{
$product = null;
if ($productInfo instanceof Product) {
$product = $productInfo;
if (!$product->getId()) {
throw new LocalizedException(
__("The product wasn't found. Verify the product and try again.")
);
}
} elseif (is_int($productInfo) || is_string($productInfo)) {
$storeId = $this->storeManager->getStore()->getId();
try {
$product = $this->productRepository->getById($productInfo, false, $storeId);
} catch (NoSuchEntityException $e) {
throw new LocalizedException(
__("The product wasn't found. Verify the product and try again."),
$e
);
}
} else {
throw new LocalizedException(
__("The product wasn't found. Verify the product and try again.")
);
}
$currentWebsiteId = $this->storeManager->getStore()->getWebsiteId();
if (!is_array($product->getWebsiteIds()) || !in_array($currentWebsiteId, $product->getWebsiteIds())) {
throw new LocalizedException(
__("The product wasn't found. Verify the product and try again.")
);
}
return $product;
}

/**
* Get request quantity
*
* @param Product $product
* @param DataObject|int|array $request
* @return int|DataObject
*/
protected function getQtyRequest($product, $request = 0)
{
$request = $this->getProductRequest($request);
$stockItem = $this->stockRegistry->getStockItem($product->getId(), $product->getStore()->getWebsiteId());
$minimumQty = $stockItem->getMinSaleQty();
//If product quantity is not specified in request and there is set minimal qty for it
if ($minimumQty
&& $minimumQty > 0
&& !$request->getQty()
) {
$request->setQty($minimumQty);
}

return $request;
}

/**
* Get request for product add to cart procedure
*
* @param DataObject|int|array $requestInfo
* @return DataObject
* @throws LocalizedException
*/
protected function getProductRequest($requestInfo)
{
if ($requestInfo instanceof DataObject) {
$request = $requestInfo;
} elseif (is_numeric($requestInfo)) {
$request = new DataObject(['qty' => $requestInfo]);
} elseif (is_array($requestInfo)) {
$request = new DataObject($requestInfo);
} else {
throw new LocalizedException(
__('We found an invalid request for adding product to quote.')
);
}
$this->getRequestInfoFilter()->filter($request);

return $request;
}

/**
* Getter for RequestInfoFilter
*
* @return RequestInfoFilterInterface
*/
protected function getRequestInfoFilter()
{
if ($this->requestInfoFilter === null) {
$this->requestInfoFilter = \Magento\Framework\App\ObjectManager::getInstance()
->get(RequestInfoFilterInterface::class);
}
return $this->requestInfoFilter;
}
}
Loading

0 comments on commit adfa7e8

Please sign in to comment.