SES will perform redirects for search terms, if
You can easily add additional logic to also perform redirects e.g. for EAN or catalog numbers.
SES has a redirect handler, which is called in the frontend search controller:
class SearchRedirectHandler
{
public function getRedirect($searchTerm, SynonymStruct $synonym = null)
{
/** @var SearchRedirectInterface $redirect */
foreach ($this->redirectServices as $redirect) {
if ($url = $redirect->getRedirect($searchTerm, $synonym)) {
return $url;
}
}
return null;
}
}
It will iterate all redirect implementations and return the first URL which is returned by one of the implementations.
Each implementation has to implement SearchRedirectInterface:
interface SearchRedirectInterface
{
/**
* Returns a redirect URL if a redirect should be performed - or NULL if no redirect can be determined in the
* current implementation
*
* @param $searchTerm
* @param SynonymStruct|null $synonym
* @return string|null
*/
public function getRedirect($searchTerm, SynonymStruct $synonym = null);
}
The synonym redirect implementation, for example, is quite simple:
// \SwagEnterpriseSearch\Bundle\EnterpriseSearchBundle\SearchRedirect\SynonymRedirect
class SynonymRedirect implements SearchRedirectInterface
{
public function getRedirect($searchTerm, SynonymStruct $synonym = null)
{
if ($synonym && $synonym->getRedirectUrl()) {
return $synonym->getRedirectUrl();
}
}
}
It will just check, if a valid synonym was passed and if it contains a redirect - and return that if it does.
Other implementations could be more complex, such as the ProductNumberRedirect:
// \SwagEnterpriseSearch\Bundle\EnterpriseSearchBundle\SearchRedirect\ProductNumberRedirect
class ProductNumberRedirect implements SearchRedirectInterface
{
private $connection;
private $router;
private $contextService;
public function __construct(Connection $connection, $router, ContextServiceInterface $contextService) { … }
public function getRedirect($searchTerm, SynonymStruct $synonym = null)
{
$result = $this->getArticleByNumber($searchTerm);
if (!$result) {
return;
}
$assembleParams = [
'sViewport' => 'detail',
'sArticle' => $result['articleId'],
];
// if variant is not the main variant, add the number to the URL
if ($result['kind'] != 1) {
$assembleParams['number'] = $result['number'];
}
return $this->router->assemble($assembleParams);
}
private function getArticleByNumber($searchTerm)
{
$result = $this->connection->fetchAll(
'SELECT
ad.ordernumber as number,
ad.articleID as articleId,
ad.kind
FROM s_articles_details ad
INNER JOIN s_categories c
ON c.id = :mainCategory
AND c.active = 1
INNER JOIN s_articles_categories_ro ro
ON ro.articleID = ad.articleID
AND ro.categoryID = c.id
WHERE `ordernumber` LIKE :number
LIMIT 1',
['number' => $searchTerm, 'mainCategory' => $this->contextService->getShopContext()->getShop()->getCategory(
)->getId()]
);
return array_shift($result);
}
}
In this case the service will query the database in order to find a matching product and then assemble a SEO url which is returned.
If you need to implement own redirect services (e.g. for catalog numbers which are stored as product attributes),
you just need to create a service implementing SearchRedirectInterface as described above.
Then register it in your services.xml using the tag enterprise_search.redirect. This could look like this:
<service id="swag_enterprise_search.redirect.catalog_redirect" class="SwagEnterpriseSearch\Bundle\EnterpriseSearchBundle\SearchRedirect\CatalogRedirect">
<argument type="service" id="dbal_connection" />
<tag name="enterprise_search.redirect" />
</service>
The tag will make sure, that your service is handled by SearchRedirectHandler.
As SearchRedirectHandler will only handle the first URL returned by one of the implementations, the order of the services
is critical: Do you want product numbers or catalog numbers to be more important?
For that reason, the enterprise_search.redirect tag support priorities: The higher the priority is, the earlier the
corresponding handler will be executed.
<service id="swag_enterprise_search.redirect.product_number" class="SwagEnterpriseSearch\Bundle\EnterpriseSearchBundle\SearchRedirect\ProductNumberRedirect">
<argument type="service" id="dbal_connection" />
<argument type="service" id="router" />
<argument type="service" id="shopware_storefront.context_service" />
<tag name="enterprise_search.redirect" priority="20" />
</service>
<service id="swag_enterprise_search.redirect.synonym" class="SwagEnterpriseSearch\Bundle\EnterpriseSearchBundle\SearchRedirect\SynonymRedirect">
<argument type="service" id="dbal_connection" />
<tag name="enterprise_search.redirect" priority="10" />
</service>
As you can see, ProductNumberRedirect has a priority of 20, SynonymRedirect has a priority of 10. In order to make
your CatalogRedirect more important thant the ProductNumberRedirect you need to give it at least a priority of 21.
If you want to place it between ProductNumberRedirect and SynonymRedirect, it needs a priority between 10 and 20.