-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add basic implementation with hardcoded products and default filters
- Loading branch information
1 parent
22dd907
commit 9b964df
Showing
26 changed files
with
1,676 additions
and
1 deletion.
There are no files selected for viewing
63 changes: 63 additions & 0 deletions
63
src/Core/Content/Product/SalesChannel/Listing/ProductListingFeaturesSubscriber.php
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,63 @@ | ||
<?php | ||
|
||
namespace Nosto\NostoIntegration\Core\Content\Product\SalesChannel\Listing; | ||
|
||
use Shopware\Core\Content\Product\Events\ProductListingCriteriaEvent; | ||
use Shopware\Core\Content\Product\Events\ProductListingResultEvent; | ||
use Shopware\Core\Content\Product\Events\ProductSearchCriteriaEvent; | ||
use Shopware\Core\Content\Product\Events\ProductSearchResultEvent; | ||
use Shopware\Core\Content\Product\Events\ProductSuggestCriteriaEvent; | ||
use Shopware\Core\Content\Product\SalesChannel\Listing\ProductListingFeaturesSubscriber as ShopwareProductListingFeaturesSubscriber; | ||
use Symfony\Component\EventDispatcher\EventSubscriberInterface; | ||
|
||
class ProductListingFeaturesSubscriber implements EventSubscriberInterface | ||
{ | ||
public function __construct( | ||
protected readonly ShopwareProductListingFeaturesSubscriber $decorated | ||
) { | ||
} | ||
|
||
public static function getSubscribedEvents(): array | ||
{ | ||
return [ | ||
ProductListingCriteriaEvent::class => 'prepare', | ||
ProductSuggestCriteriaEvent::class => 'prepare', | ||
ProductSearchCriteriaEvent::class => [ | ||
['handleSearchRequest', 100], | ||
], | ||
ProductListingResultEvent::class => 'process', | ||
ProductSearchResultEvent::class => 'process', | ||
]; | ||
} | ||
|
||
public function prepare(ProductListingCriteriaEvent $event): void | ||
{ | ||
$this->decorated->prepare($event); | ||
} | ||
|
||
public function process(ProductListingResultEvent $event): void | ||
{ | ||
$this->decorated->process($event); | ||
} | ||
|
||
public function handleSearchRequest(ProductSearchCriteriaEvent $event): void | ||
{ | ||
$limit = $event->getCriteria()->getLimit(); | ||
$this->decorated->prepare($event); | ||
|
||
$limitOverride = $limit ?? $event->getCriteria()->getLimit(); | ||
|
||
$event->getCriteria()->setIds(['2a88d9b59d474c7e869d8071649be43c', '11dc680240b04f469ccba354cbf0b967']); | ||
|
||
// $this->findologicSearchService->doSearch($event, $limitOverride); | ||
} | ||
|
||
public function __call($method, $args) | ||
{ | ||
if (!method_exists($this->decorated, $method)) { | ||
return; | ||
} | ||
|
||
return $this->decorated->{$method}(...$args); | ||
} | ||
} |
108 changes: 108 additions & 0 deletions
108
src/Core/Content/Product/SalesChannel/Search/ProductSearchRoute.php
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 | ||
|
||
namespace Nosto\NostoIntegration\Core\Content\Product\SalesChannel\Search; | ||
|
||
use Nosto\NostoIntegration\Model\ConfigProvider; | ||
use Nosto\NostoIntegration\Traits\SearchResultHelper; | ||
use Shopware\Core\Content\Product\Aggregate\ProductVisibility\ProductVisibilityDefinition; | ||
use Shopware\Core\Content\Product\Events\ProductSearchCriteriaEvent; | ||
use Shopware\Core\Content\Product\Events\ProductSearchResultEvent; | ||
use Shopware\Core\Content\Product\ProductDefinition; | ||
use Shopware\Core\Content\Product\SalesChannel\Listing\ProductListingResult; | ||
use Shopware\Core\Content\Product\SalesChannel\ProductAvailableFilter; | ||
use Shopware\Core\Content\Product\SalesChannel\Search\AbstractProductSearchRoute; | ||
use Shopware\Core\Content\Product\SalesChannel\Search\ProductSearchRouteResponse; | ||
use Shopware\Core\Content\Product\SearchKeyword\ProductSearchBuilderInterface; | ||
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; | ||
use Shopware\Core\Framework\DataAbstractionLayer\Search\EntitySearchResult; | ||
use Shopware\Core\Framework\DataAbstractionLayer\Search\RequestCriteriaBuilder; | ||
use Shopware\Core\System\SalesChannel\Entity\SalesChannelRepository; | ||
use Shopware\Core\System\SalesChannel\SalesChannelContext; | ||
use Symfony\Component\HttpFoundation\Request; | ||
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; | ||
|
||
class ProductSearchRoute extends AbstractProductSearchRoute | ||
{ | ||
use SearchResultHelper; | ||
|
||
public function __construct( | ||
private readonly AbstractProductSearchRoute $decorated, | ||
private readonly ProductSearchBuilderInterface $searchBuilder, | ||
private readonly EventDispatcherInterface $eventDispatcher, | ||
private readonly SalesChannelRepository $salesChannelProductRepository, | ||
private readonly ProductDefinition $definition, | ||
private readonly RequestCriteriaBuilder $criteriaBuilder, | ||
private readonly ConfigProvider $configProvider | ||
) { | ||
} | ||
|
||
public function getDecorated(): AbstractProductSearchRoute | ||
{ | ||
return $this->decorated; | ||
} | ||
|
||
public function load( | ||
Request $request, | ||
SalesChannelContext $context, | ||
?Criteria $criteria = null | ||
): ProductSearchRouteResponse { | ||
$this->addElasticSearchContext($context); | ||
|
||
$criteria ??= $this->criteriaBuilder->handleRequest( | ||
$request, | ||
new Criteria(), | ||
$this->definition, | ||
$context->getContext() | ||
); | ||
|
||
$shouldHandleRequest = $this->configProvider->isSearchEnabled(); | ||
|
||
$criteria->addFilter( | ||
new ProductAvailableFilter( | ||
$context->getSalesChannel()->getId(), | ||
ProductVisibilityDefinition::VISIBILITY_SEARCH | ||
) | ||
); | ||
|
||
$this->searchBuilder->build($request, $criteria, $context); | ||
|
||
$this->eventDispatcher->dispatch( | ||
new ProductSearchCriteriaEvent($request, $criteria, $context) | ||
); | ||
|
||
if (!$shouldHandleRequest) { | ||
return $this->decorated->load($request, $context, $criteria); | ||
} | ||
|
||
$query = $request->query->get('search'); | ||
$result = $this->doSearch($criteria, $context, $query); | ||
$result = ProductListingResult::createFrom($result); | ||
$result->addCurrentFilter('search', $query); | ||
|
||
$this->eventDispatcher->dispatch( | ||
new ProductSearchResultEvent($request, $result, $context) | ||
); | ||
|
||
return new ProductSearchRouteResponse($result); | ||
} | ||
|
||
protected function doSearch( | ||
Criteria $criteria, | ||
SalesChannelContext $salesChannelContext, | ||
?string $query | ||
): EntitySearchResult { | ||
$this->assignPaginationToCriteria($criteria); | ||
$this->addOptionsGroupAssociation($criteria); | ||
|
||
if (empty($criteria->getIds())) { | ||
return $this->createEmptySearchResult($criteria, $salesChannelContext->getContext()); | ||
} | ||
|
||
return $this->fetchProducts($criteria, $salesChannelContext, $query); | ||
} | ||
|
||
public function addElasticSearchContext(SalesChannelContext $context): void | ||
{ | ||
$context->getContext()->addState(Criteria::STATE_ELASTICSEARCH_AWARE); | ||
} | ||
} |
89 changes: 89 additions & 0 deletions
89
src/Core/Content/Product/SearchKeyword/ProductSearchBuilder.php
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,89 @@ | ||
<?php | ||
|
||
namespace Nosto\NostoIntegration\Core\Content\Product\SearchKeyword; | ||
|
||
use Shopware\Core\Content\Product\SearchKeyword\ProductSearchBuilderInterface; | ||
use Shopware\Core\Content\Product\SearchKeyword\ProductSearchTermInterpreterInterface; | ||
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; | ||
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\AndFilter; | ||
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\ContainsFilter; | ||
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsAnyFilter; | ||
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter; | ||
use Shopware\Core\Framework\DataAbstractionLayer\Search\Query\ScoreQuery; | ||
use Shopware\Core\Framework\DataAbstractionLayer\Search\Term\SearchPattern; | ||
use Shopware\Core\System\SalesChannel\SalesChannelContext; | ||
use Symfony\Component\HttpFoundation\Request; | ||
|
||
/** | ||
* @see \Shopware\Core\Content\Product\SearchKeyword\ProductSearchBuilder | ||
*/ | ||
class ProductSearchBuilder implements ProductSearchBuilderInterface | ||
{ | ||
public function __construct( | ||
private readonly ProductSearchTermInterpreterInterface $interpreter, | ||
private readonly ProductSearchBuilderInterface $decorated | ||
) { | ||
} | ||
|
||
public function build(Request $request, Criteria $criteria, SalesChannelContext $context): void | ||
{ | ||
if ($request->getPathInfo() === '/suggest') { | ||
$this->buildParent($request, $criteria, $context); | ||
return; | ||
} | ||
|
||
$this->doBuild($request, $criteria, $context); | ||
} | ||
|
||
public function buildParent(Request $request, Criteria $criteria, SalesChannelContext $context): void | ||
{ | ||
$this->decorated->build($request, $criteria, $context); | ||
} | ||
|
||
public function doBuild(Request $request, Criteria $criteria, SalesChannelContext $context): void | ||
{ | ||
$search = $request->get('search'); | ||
|
||
if (is_array($search)) { | ||
$term = implode(' ', $search); | ||
} else { | ||
$term = (string) $search; | ||
} | ||
|
||
$term = trim($term); | ||
$pattern = $this->interpreter->interpret($term, $context->getContext()); | ||
|
||
foreach ($pattern->getTerms() as $searchTerm) { | ||
$criteria->addQuery( | ||
new ScoreQuery( | ||
new EqualsFilter('product.searchKeywords.keyword', $searchTerm->getTerm()), | ||
$searchTerm->getScore(), | ||
'product.searchKeywords.ranking' | ||
) | ||
); | ||
} | ||
$criteria->addQuery( | ||
new ScoreQuery( | ||
new ContainsFilter('product.searchKeywords.keyword', $pattern->getOriginal()->getTerm()), | ||
$pattern->getOriginal()->getScore(), | ||
'product.searchKeywords.ranking' | ||
) | ||
); | ||
|
||
if ($pattern->getBooleanClause() !== SearchPattern::BOOLEAN_CLAUSE_AND) { | ||
$criteria->addFilter(new AndFilter([ | ||
new EqualsAnyFilter('product.searchKeywords.keyword', array_values($pattern->getAllTerms())), | ||
new EqualsFilter('product.searchKeywords.languageId', $context->getContext()->getLanguageId()), | ||
])); | ||
|
||
return; | ||
} | ||
|
||
foreach ($pattern->getTokenTerms() as $terms) { | ||
$criteria->addFilter(new AndFilter([ | ||
new EqualsFilter('product.searchKeywords.languageId', $context->getContext()->getLanguageId()), | ||
new EqualsAnyFilter('product.searchKeywords.keyword', $terms), | ||
])); | ||
} | ||
} | ||
} |
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,48 @@ | ||
<?php | ||
|
||
namespace Nosto\NostoIntegration\Elasticsearch\Product; | ||
|
||
use Shopware\Core\Content\Product\ProductDefinition; | ||
use Shopware\Core\Content\Product\SearchKeyword\ProductSearchBuilderInterface; | ||
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; | ||
use Shopware\Core\System\SalesChannel\SalesChannelContext; | ||
use Shopware\Elasticsearch\Framework\ElasticsearchHelper; | ||
use Symfony\Component\HttpFoundation\Request; | ||
|
||
/** | ||
* @see \Shopware\Elasticsearch\Product\ProductSearchBuilder | ||
*/ | ||
class ProductSearchBuilder implements ProductSearchBuilderInterface | ||
{ | ||
public function __construct( | ||
private readonly ProductSearchBuilderInterface $decorated, | ||
private readonly ElasticsearchHelper $helper, | ||
private readonly ProductDefinition $productDefinition | ||
) { | ||
} | ||
|
||
public function build(Request $request, Criteria $criteria, SalesChannelContext $context): void | ||
{ | ||
if (!$this->helper->allowSearch($this->productDefinition, $context->getContext(), $criteria)) { | ||
$this->decorated->build($request, $criteria, $context); | ||
|
||
return; | ||
} | ||
|
||
$search = $request->get('search'); | ||
|
||
if (\is_array($search)) { | ||
$term = implode(' ', $search); | ||
} else { | ||
$term = (string) $search; | ||
} | ||
|
||
$term = trim($term); | ||
|
||
// reset queries and set term to criteria. | ||
$criteria->resetQueries(); | ||
|
||
// elasticsearch will interpret this on demand | ||
$criteria->setTerm($term); | ||
} | ||
} |
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
Oops, something went wrong.