Amasty SEO Toolkit: filter redirects in SQL instead of loading them all into PHP
================================================================================
Package: amasty/module-seo-toolkit-lite (tested on 1.3.0)
Author: Paxento, https://paxento.com/
Article: https://paxento.com/magento-redirects-performance-amasty-seo-toolkit/
License: provided as-is, without warranty. Test on staging before production.
What it changes
- RedirectGetter asks the database only for redirects whose request_path can
match the current path, instead of loading every active redirect of the store
and testing each one in PHP.
- Adds an index on amasty_seotoolkit_redirect.request_path for the LIKE prefix
filter. Run bin/magento setup:upgrade after applying.
Known limits
- request_path is evaluated as a regular expression. A path with an unbalanced
")" or other regex syntax makes the query fail; keep such paths in core URL
rewrites instead.
- The prefix filter uses the first path segment of the request, so a wildcard
rule must start with that full segment (e.g. "brands/*" works, "brand*" does
not match "brands/...").
Apply with cweagans/composer-patches or vaimo/composer-patches, e.g.:
"patches": {
"amasty/module-seo-toolkit-lite": {
"Filter redirects in SQL": "patches/amasty-seo-toolkit-redirect-router-performance.patch"
}
}
diff --git a/Model/Redirect/RedirectGetter.php b/Model/Redirect/RedirectGetter.php
index ff74c61..56b86b5 100644
--- a/Model/Redirect/RedirectGetter.php
+++ b/Model/Redirect/RedirectGetter.php
@@ -44,7 +44,7 @@ class RedirectGetter
*/
public function getRedirect(string $path): ?RedirectInterface
{
- $collection = $this->getCollection();
+ $collection = $this->getCollection($path);
$resultRedirect = null;
foreach ($collection as $redirect) {
if ($this->isValidRedirect($redirect->getRequestPath(), $path)) {
@@ -71,12 +71,14 @@ class RedirectGetter
}
/**
+ * @param string $requestPath
* @return \Amasty\SeoToolkitLite\Model\ResourceModel\Redirect\Collection|void
*/
- private function getCollection()
+ private function getCollection(string $requestPath)
{
return $this->collectionFactory->create()
- ->addFieldToFilter(RedirectInterface::STATUS, 1)
+ ->addRequestPathFilter($requestPath)
+ ->addStatusFilter(1)
->addStoreFilter((int)$this->storeManager->getStore()->getId())
->setOrders([
RedirectInterface::PRIORITY => Collection::SORT_ORDER_ASC,
diff --git a/Model/ResourceModel/Redirect/Collection.php b/Model/ResourceModel/Redirect/Collection.php
index 7582e82..fde34f6 100644
--- a/Model/ResourceModel/Redirect/Collection.php
+++ b/Model/ResourceModel/Redirect/Collection.php
@@ -50,4 +50,35 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab
return $this;
}
+
+ /**
+ * @param int $status
+ * @return $this
+ */
+ public function addStatusFilter(int $status): self
+ {
+ $this->addFieldToFilter(RedirectInterface::STATUS, $status);
+
+ return $this;
+ }
+
+ /**
+ * @param string $requestPath
+ * @return $this
+ */
+ public function addRequestPathFilter(string $requestPath): self
+ {
+ $requestPath = trim($requestPath, '/');
+ $condition = new \Zend_Db_Expr("? REGEXP CONCAT('^', REPLACE(main_table.request_path, '*', '[[:alnum:]]*'))");
+
+ $select = $this->getSelect()
+ ->where($condition, $requestPath)
+ ;
+
+ if ($path = explode('/', $requestPath)[0] ?? '') {
+ $this->addFieldToFilter('main_table.request_path', ['like' => "{$path}%"]);
+ };
+
+ return $this;
+ }
}
diff --git a/etc/db_schema.xml b/etc/db_schema.xml
index c73e610..ac2d305 100644
--- a/etc/db_schema.xml
+++ b/etc/db_schema.xml
@@ -25,6 +25,9 @@