Skip to content
Merged
36 changes: 36 additions & 0 deletions Block/Adminhtml/System/Config/ApplicationId.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace Algolia\SearchAdapter\Block\Adminhtml\System\Config;

use Algolia\SearchAdapter\Helper\ConfigHelper;
use Magento\Backend\Block\Template\Context;
use Magento\Framework\Data\Form\Element\AbstractElement;
use Magento\Framework\View\Helper\SecureHtmlRenderer;

class ApplicationId extends \Magento\Config\Block\System\Config\Form\Field
{
public function __construct(
protected ConfigHelper $configHelper,
Context $context,
array $data = [],
?SecureHtmlRenderer $secureRenderer = null
) {
parent::__construct($context, $data, $secureRenderer);
}

protected function _getElementHtml(AbstractElement $element): string
{
$element->setReadonly(true);

$request = $this->getRequest();
$websiteId = $request->getParam('website') ?? null;
$storeId = $request->getParam('store') ?? null;

return '<strong>' . $this->configHelper->getApplicationId($websiteId, $storeId) . '</strong>';
}

protected function _isInheritCheckboxRequired(AbstractElement $element): bool
{
return false;
}
}
45 changes: 45 additions & 0 deletions Block/Adminhtml/System/Config/Engine.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

namespace Algolia\SearchAdapter\Block\Adminhtml\System\Config;

use Algolia\SearchAdapter\Helper\ConfigHelper;
use Magento\Backend\Block\Template\Context;
use Magento\Framework\Data\Form\Element\AbstractElement;
use Magento\Framework\View\Helper\SecureHtmlRenderer;

class Engine extends \Magento\Config\Block\System\Config\Form\Field
{
public function __construct(
protected ConfigHelper $configHelper,
Context $context,
array $data = [],
?SecureHtmlRenderer $secureRenderer = null
) {
parent::__construct($context, $data, $secureRenderer);
}

/** Hide any engine other than Algolia if not in default scope */
protected function _decorateRowHtml(AbstractElement $element, $html): string
{
$hiddenStyleAttr = '';
if (!$this->configHelper->isEngineSelectVisible($this->getRequest())) {
$hiddenStyleAttr = ' style="display:none;"';
}
return '<tr id="row_' . $element->getHtmlId() . '"'. $hiddenStyleAttr . '>' . $html . '</tr>';
}

/** Disable engine selection for anything other than default scope */
protected function _getElementHtml(AbstractElement $element): string
{
if (!$this->configHelper->isEngineSelectEnabled($this->getRequest())) {
$element->setReadonly(true);
}
return parent::_getElementHtml($element);
}

/** Preserve engine scope limitation despite overriding scope restrictions */
protected function _isInheritCheckboxRequired(AbstractElement $element): bool
{
return false;
}
}
47 changes: 47 additions & 0 deletions Block/Adminhtml/System/Config/TestConnection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

namespace Algolia\SearchAdapter\Block\Adminhtml\System\Config;

class TestConnection extends \Magento\AdvancedSearch\Block\Adminhtml\System\Config\TestConnection
{
protected function _getFieldMapping(): array
{
$fields = [
'connectTimeout' => 'catalog_search_algolia_connect_timeout',
'readTimeout' => 'catalog_search_algolia_read_timeout',
];

return array_merge(parent::_getFieldMapping(), $fields);
}

/** Augment AJAX requests to include website and store scope */
protected function _getElementHtml(\Magento\Framework\Data\Form\Element\AbstractElement $element): string
{
$request = $this->getRequest();
$params = [];
if ($website = $request->getParam('website')) {
$params['website'] = $website;
}
elseif ($store = $request->getParam('store')) {
$params['store'] = $store;
}

$originalData = $element->getOriginalData();
$this->addData(
[
'button_label' => __($originalData['button_label']),
'html_id' => $element->getHtmlId(),
'ajax_url' => $this->_urlBuilder->getUrl('catalog/search_system_config/testconnection', $params),
'field_mapping' => str_replace('"', '\\"', json_encode($this->_getFieldMapping()))
]
);

return $this->_toHtml();
}


public function getButtonLabel(): string
{
return 'Test Algolia Connection';
}
}
78 changes: 76 additions & 2 deletions Helper/ConfigHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,23 @@

namespace Algolia\SearchAdapter\Helper;

use Algolia\AlgoliaSearch\Helper\ConfigHelper as BaseConfigHelper;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\RequestInterface;
use Magento\Framework\Search\EngineResolverInterface;
use Magento\Store\Model\ScopeInterface;

class ConfigHelper
{
public const ALGOLIA_ENGINE = "algolia";

public const CONNECTION_TIMEOUT = 'catalog/search/algolia_connect_timeout';
public const READ_TIMEOUT = 'catalog/search/algolia_read_timeout';

public function __construct(
protected EngineResolverInterface $engineResolver
){}
protected EngineResolverInterface $engineResolver,
protected ScopeConfigInterface $configInterface,
) {}

/**
* @return bool
Expand All @@ -19,4 +27,70 @@ public function isAlgoliaEngineSelected(): bool
{
return $this->engineResolver->getCurrentSearchEngine() === self::ALGOLIA_ENGINE;
}

public function isEngineSelectEnabled(RequestInterface $request): bool
{
return !$request->getParam('store') && !$request->getParam('website');
}

public function isEngineSelectVisible(RequestInterface $request): bool {
return
$this->isEngineSelectEnabled($request)
||
!$this->isEngineSelectEnabled($request) && $this->isAlgoliaEngineSelected();
}

/**
* Get value scoped by website or store
*/
protected function getConfigByScope(string $path, ?int $websiteId = null, ?int $storeId = null): string
{

if ($websiteId !== null) {
$scope = ScopeInterface::SCOPE_WEBSITES;
$scopeId = $websiteId;
} elseif ($storeId !== null) {
$scope = ScopeInterface::SCOPE_STORES;
$scopeId = $storeId;
} else {
$scope = ScopeConfigInterface::SCOPE_TYPE_DEFAULT;
$scopeId = null;
}
return $this->configInterface->getValue($path, $scope, $scopeId);
}

/**
* Get admin scoped App ID
*/
public function getApplicationID(?int $websiteId = null, ?int $storeId = null): string
{
return $this->getConfigByScope(BaseConfigHelper::APPLICATION_ID, $websiteId, $storeId);
}

/**
* Get admin scoped API key
*/
public function getApiKey(?int $websiteId = null, ?int $storeId = null): string
{
return $this->getConfigByScope(BaseConfigHelper::API_KEY, $websiteId, $storeId);
}

/**
* Backend search uses different timeout settings from indexer
* Only used by frontend not adminhtml scoped
*/
public function getConnectionTimeout(?int $storeId = null): int
{
return $this->getConfigByScope(self::CONNECTION_TIMEOUT, null, $storeId);
}

/**
* Backend search uses different timeout settings from indexer
* Only used by frontend not adminhtml scoped
*/
public function getReadTimeout(?int $storeId = null): int
{
return $this->getConfigByScope(self::READ_TIMEOUT, null, $storeId);
}

}
21 changes: 12 additions & 9 deletions Model/Adapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

namespace Algolia\SearchAdapter\Model;

use Algolia\AlgoliaSearch\Service\AlgoliaConnector;
use Algolia\AlgoliaSearch\Exceptions\AlgoliaException;
use Algolia\SearchAdapter\Model\Request\QueryMapper;
use Algolia\SearchAdapter\Model\Response\DocumentMapper;;

use Algolia\SearchAdapter\Service\AlgoliaBackendConnector;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\Search\AdapterInterface;
use Magento\Framework\Search\RequestInterface;
use Magento\Framework\Search\Response\QueryResponse;
Expand All @@ -19,17 +20,19 @@
class Adapter implements AdapterInterface
{
public function __construct(
protected AlgoliaConnector $connector,
protected QueryMapper $queryMapper,
protected DocumentMapper $documentMapper,
protected ResponseFactory $responseFactory,
protected AggregationBuilder $aggregationBuilder,
protected QueryContainerFactory $queryContainerFactory,
protected Mapper $mapper,
protected AlgoliaBackendConnector $connector,
protected QueryMapper $queryMapper,
protected DocumentMapper $documentMapper,
protected ResponseFactory $responseFactory,
protected AggregationBuilder $aggregationBuilder,
protected QueryContainerFactory $queryContainerFactory,
protected Mapper $mapper,
){}

/**
* @inheritDoc
*
* @throws NoSuchEntityException|AlgoliaException
*/
public function query(RequestInterface $request): QueryResponse
{
Expand Down
42 changes: 42 additions & 0 deletions Model/Config.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

namespace Algolia\SearchAdapter\Model;

use Algolia\SearchAdapter\Helper\ConfigHelper;
use Magento\AdvancedSearch\Model\Client\ClientOptionsInterface;

class Config implements ClientOptionsInterface
{
const DEFAULT_TIMEOUT_CONNECT = 2;
const DEFAULT_TIMEOUT_READ = 5;

public function __construct(
protected ConfigHelper $configHelper
) {}

/**
* @inheritdoc
*/
public function prepareClientOptions($options = []): array
{
$storeId = $options['store'] ?? null;
$websiteId = $options['website'] ?? null;

$defaultOptions = [
'applicationId' => $this->configHelper->getApplicationId($websiteId, $storeId),
'apiKey' => $this->configHelper->getApiKey($websiteId, $storeId),
'connectTimeout' => self::DEFAULT_TIMEOUT_CONNECT,
'readTimeout' => self::DEFAULT_TIMEOUT_READ
];
$options = array_merge($defaultOptions, $options);
$allowedOptions = array_merge(array_keys($defaultOptions), ['engine']);

return array_filter(
$options,
function (string $key) use ($allowedOptions) {
return in_array($key, $allowedOptions);
},
ARRAY_FILTER_USE_KEY
);
}
}
27 changes: 27 additions & 0 deletions Model/Config/Comment/ApplicationIdComment.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace Algolia\SearchAdapter\Model\Config\Comment;

use \Algolia\AlgoliaSearch\Model\Config\AbstractConfigComment;

class ApplicationIdComment extends AbstractConfigComment
{
public function getCommentText($elementValue): string
{
$link = $this->getConfigLink(
'algoliasearch_credentials',
'algoliasearch_credentials_credentials-link',
true
);

return <<<COMMENT
Algolia's backend search supports multiple application IDs on a single Magento instance.

<br/><br/>

To configure credentials for your application id within a given scope, go to
<a href="$link">Algolia Search > Credentials and Basic Setup</a>

COMMENT;
}
}
5 changes: 1 addition & 4 deletions Model/Request/QueryMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
use Algolia\AlgoliaSearch\Api\Data\IndexOptionsInterface;
use Algolia\AlgoliaSearch\Api\Data\SearchQueryInterface;
use Algolia\AlgoliaSearch\Api\Data\SearchQueryInterfaceFactory;
use Algolia\AlgoliaSearch\Exceptions\AlgoliaException;
use Algolia\AlgoliaSearch\Service\Product\IndexOptionsBuilder;
use Magento\Framework\App\ScopeResolverInterface;
use Magento\Framework\Exception\NoSuchEntityException;
Expand All @@ -27,7 +26,6 @@ public function __construct(

/**
* @throws NoSuchEntityException
* @throws AlgoliaException
*/
public function buildQuery(RequestInterface $request): SearchQueryInterface
{
Expand All @@ -40,7 +38,6 @@ public function buildQuery(RequestInterface $request): SearchQueryInterface

/**
* @throws NoSuchEntityException
* @throws AlgoliaException
*/
protected function getIndexOptions(RequestInterface $request): IndexOptionsInterface
{
Expand Down Expand Up @@ -91,7 +88,7 @@ protected function getParams(RequestInterface $request): array
return $params;
}

protected function getParam(BoolQuery $query, string $key)
protected function getParam(BoolQuery $query, string $key): string
{
$must = $query->getMust();
if (!array_key_exists($key, $must)) {
Expand Down
16 changes: 9 additions & 7 deletions Model/Response/DocumentMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ public function buildDocuments(array $searchResponse): array
{
$i = 0;
return array_map(
fn(array $hit) => [
'fields' => [
'_id' => [ $hit['objectID'] ],
],
'score' => null,
'sort' => [ ++$i, $hit['objectID'] ]
],
function(array $hit) use (&$i) {
return [
'fields' => [
'_id' => [ $hit['objectID'] ],
],
'score' => null,
'sort' => [ ++$i, $hit['objectID'] ]
];
},
$this->extractHits($searchResponse)
);
}
Expand Down
Loading