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 @@ + + +