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.