<?php
declare(strict_types=1);
namespace Swag\EnterpriseSearch\Action;
use Shopware\Core\Framework\Api\Context\SalesChannelApiSource;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsAnyFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\MultiFilter;
use Swag\EnterpriseSearch\Action\ActionSearchTerm\ActionSearchTermCollection;
use Swag\EnterpriseSearch\Action\ActionType\ActionTypeEntityInterface;
use Swag\EnterpriseSearch\Action\ActionType\ActionTypeProvider;
use Swag\EnterpriseSearch\Action\ActionType\InvalidActionTypeException;
use Swag\EnterpriseSearch\Action\ActionType\InvalidActionTypeFieldsException;
use Swag\EnterpriseSearch\Relevance\TokenizerInterface;
class ActionSearchProcessor
{
public const STATE_ELASTICSEARCH_AWARE = 'elasticsearchAware';
/**
* @var EntityRepositoryInterface
*/
private $actionSearchTermRepository;
/**
* @var ActionTypeProvider
*/
private $actionTypeProvider;
/**
* @var TokenizerInterface
*/
private $tokenizer;
/**
* @var ActionCollection[]|null
*/
private $cachedActions;
public function __construct(
EntityRepositoryInterface $actionSearchTermRepository,
ActionTypeProvider $actionTypeProvider,
TokenizerInterface $tokenizer
) {
$this->actionSearchTermRepository = $actionSearchTermRepository;
$this->actionTypeProvider = $actionTypeProvider;
$this->tokenizer = $tokenizer;
}
public function applySearchActions(Context $context, string $searchTerm, ActionProcessorEnvironmentInterface $actionExecution): void
{
$source = $context->getSource();
if (!($source instanceof SalesChannelApiSource)) {
return;
}
$matchingActions = $this->getMatchingActions($context, $searchTerm);
foreach ($matchingActions as $action) {
$actionTypeProcessor = $this->actionTypeProvider->getActionType($action->getType());
if (!$actionTypeProcessor) {
continue;
}
$actionTypeEntityClass = $actionTypeProcessor->getEntityClass();
if (!class_exists($actionTypeEntityClass) || !is_a($actionTypeEntityClass, ActionTypeEntityInterface::class, true)) {
throw new InvalidActionTypeException('Invalid ActionType entity class provided.');
}
try {
$actionTypeEntity = $actionTypeEntityClass::fromTypeFieldsArray($action->getTypeFields());
} catch (InvalidActionTypeFieldsException $invalidActionTypeFieldsException) {
continue;
}
$actionExecution->executeAction($action, $actionTypeEntity, $actionTypeProcessor);
}
}
private function getMatchingActions(Context $context, string $searchTerm): ActionCollection
{
if ($this->cachedActions !== null && isset($this->cachedActions[$searchTerm])) {
return $this->cachedActions[$searchTerm];
}
$matchingActions = new ActionCollection();
$matchingSearchTerms = $this->getMatchingSearchTerms($context, $searchTerm);
foreach ($matchingSearchTerms as $matchingSearchTerm) {
$action = $matchingSearchTerm->getAction();
if (!$action->isValid()) {
continue;
}
$matchingActions->add($action);
}
$this->cachedActions[$searchTerm] = $matchingActions;
return $matchingActions;
}
private function getMatchingSearchTerms(Context $context, string $searchTerm): ActionSearchTermCollection
{
$context = clone $context;
/** @var SalesChannelApiSource $source */
$source = $context->getSource();
$criteria = new Criteria();
if (method_exists($context, 'addState')) {
$context->addState(self::STATE_ELASTICSEARCH_AWARE);
$criteria->setTerm($searchTerm);
} else {
$splitSearchTerms = $this->tokenizer->tokenize($searchTerm);
$criteria->addFilter(new MultiFilter(MultiFilter::CONNECTION_OR, [
new EqualsFilter('term', $searchTerm),
new EqualsAnyFilter('term', $splitSearchTerms),
]));
}
$criteria->addFilter(new EqualsFilter('action.active', true));
$criteria->addFilter(new EqualsFilter('action.salesChannelId', $source->getSalesChannelId()));
$criteria->addAssociation('action');
$searchResult = $this->actionSearchTermRepository->search($criteria, $context);
/** @var ActionSearchTermCollection $actionSearchTermCollection */
$actionSearchTermCollection = $searchResult->getEntities();
return $actionSearchTermCollection;
}
}