<?php
namespace H1web\Blog\Subscriber;
use H1web\Blog\Blog\BlogProductListingResult;
use H1web\Blog\Blog\SalesChannel\BlogAvailableFilter;
use H1web\Blog\Blog\SearchKeyword\BlogSearchTermInterpreter;
use Shopware\Core\Content\Product\SalesChannel\Listing\ProductListingResult;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
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\RequestCriteriaBuilder;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Term\SearchPattern;
use Shopware\Core\Framework\Routing\Exception\MissingRequestParameterException;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Shopware\Core\System\SystemConfig\SystemConfigService;
use Shopware\Storefront\Page\Search\SearchPageLoadedEvent;
use Shopware\Storefront\Page\Suggest\SuggestPageLoadedEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Symfony\Component\HttpFoundation\Request;
class Search implements EventSubscriberInterface
{
/**
* Search constructor.
* @param EntityRepositoryInterface $blogRepository
* @param RequestCriteriaBuilder $criteriaBuilder
* @param SystemConfigService $systemConfigService
* @param BlogSearchTermInterpreter $interpreter
*/
public function __construct(
EntityRepositoryInterface $blogRepository,
RequestCriteriaBuilder $criteriaBuilder,
SystemConfigService $systemConfigService,
BlogSearchTermInterpreter $interpreter
) {
$this->blogRepository = $blogRepository;
$this->criteriaBuilder = $criteriaBuilder;
$this->systemConfigService = $systemConfigService;
$this->interpreter = $interpreter;
}
/**
* @return string[]
*/
public static function getSubscribedEvents(): array
{
return [
SearchPageLoadedEvent::class => "onSearch",
SuggestPageLoadedEvent::class => "onSuggest"
];
}
//Stolen from ProductListingFeaturesSubscriber because DRY, unless ShopWare
private function getLimit(Request $request, SalesChannelContext $context): int
{
$limit = $request->query->getInt('limit', 0);
if ($request->isMethod(Request::METHOD_POST)) {
$limit = $request->request->getInt('limit', $limit);
}
$limit = $limit > 0 ? $limit : $this->systemConfigService->getInt('core.listing.productsPerPage', $context->getSalesChannel()->getId());
return $limit <= 0 ? 24 : $limit;
}
//Stolen from ProductListingFeaturesSubscriber because DRY, unless ShopWare
private function getPage(Request $request): int
{
$page = $request->query->getInt('p', 1);
if ($request->isMethod(Request::METHOD_POST)) {
$page = $request->request->getInt('p', $page);
}
return $page <= 0 ? 1 : $page;
}
private function getCriteria(Request $request, SalesChannelContext $context): Criteria
{
$criteria = new Criteria();
$criteria->addFilter(new BlogAvailableFilter($context->getSalesChannelId(), $request->cookies->get("timezone")));
//Stolen from ProductSearchBuilder
$search = $request->get('search');
if (is_array($search)) {
$term = implode(' ', $search);
} else {
$term = (string) $search;
}
$term = trim($term);
if (empty($term)) {
throw new MissingRequestParameterException('search');
}
$pattern = $this->interpreter->interpret($term, $context->getContext());
foreach ($pattern->getTerms() as $searchTerm) {
$criteria->addQuery(
new ScoreQuery(
new EqualsFilter('h1webblog_blog.searchKeywords.keyword', $searchTerm->getTerm()),
$searchTerm->getScore(),
'h1webblog_blog.searchKeywords.ranking'
)
);
}
$criteria->addQuery(
new ScoreQuery(
new ContainsFilter('h1webblog_blog.searchKeywords.keyword', $pattern->getOriginal()->getTerm()),
$pattern->getOriginal()->getScore(),
'h1webblog_blog.searchKeywords.ranking'
)
);
if ($pattern->getBooleanClause() !== SearchPattern::BOOLEAN_CLAUSE_AND) {
$criteria->addFilter(new AndFilter([
new EqualsAnyFilter('h1webblog_blog.searchKeywords.keyword', array_values($pattern->getAllTerms())),
new EqualsFilter('h1webblog_blog.searchKeywords.languageId', $context->getContext()->getLanguageId())
]));
} else {
foreach ($pattern->getTokenTerms() as $terms) {
$criteria->addFilter(new AndFilter([
new EqualsFilter('h1webblog_blog.searchKeywords.languageId', $context->getContext()->getLanguageId()),
new EqualsAnyFilter('h1webblog_blog.searchKeywords.keyword', $terms),
]));
}
}
//Stolen from ProductListingFeaturesSubscriber
$limit = $this->getLimit($request, $context);
$page = $this->getPage($request);
$criteria->setOffset(($page - 1) * $limit);
$criteria->setLimit($limit);
$criteria->setTotalCountMode(Criteria::TOTAL_COUNT_MODE_EXACT);
$criteria->addAssociation('categories')
->addAssociation('visibilities')
->addAssociation('tags')
->addAssociation('customFields')
->addAssociation('properties')
->addAssociation('properties.group');
return $criteria;
}
public function onSearch(SearchPageLoadedEvent $event): void
{
$saleChannelContext = $event->getSalesChannelContext();
$salesChannelId= $saleChannelContext->getSalesChannel()->getId();
if ($this->systemConfigService->get('H1webBlog.config.blogSearchResults', $salesChannelId)) {
return;
}
$page = $event->getPage();
$criteria = $this->getCriteria($event->getRequest(), $event->getSalesChannelContext());
$blogs = $this->blogRepository->search($criteria, $event->getContext());
$page->addExtension("blogpages", $blogs);
//Allow further pagination if need be
$listing = $page->getListing();
$total = $listing->getTotal();
if ($total < $blogs->getTotal()) {
$total = $blogs->getTotal();
}
//Hack for the silly SearchController::search assumes a product result
if ($total == 1 && $blogs->getTotal() == 1) {
$total = 2;
}
$listingOld = $listing;
//Hack to change pagination to our wishes
$listing = BlogProductListingResult::createFrom($listing);
$listing->setTotal($total);
$page->setListing($listing);
}
public function onSuggest(SuggestPageLoadedEvent $event): void
{
$page = $event->getPage();
$criteria = $this->getCriteria($event->getRequest(), $event->getSalesChannelContext());
$criteria->setLimit(10);
$blogs = $this->blogRepository->search($criteria, $event->getContext());
$page->addExtension("blogpages", $blogs);
}
}