From 119233b724afc8ee3d878f36d94c2cdb8219fa16 Mon Sep 17 00:00:00 2001 From: Marcin Nowak Date: Wed, 28 Oct 2020 19:08:46 +0100 Subject: [PATCH] Add new functionality for blocking generating rewrites for disabled product and category. Add in config field create_url_for_disabled. When it is set as false dont generate redirects for disabled products and categories, what improves performance. Default is true, thanks to it it is compatible backwards. --- .../core/Mage/Catalog/Model/Indexer/Url.php | 7 +- .../core/Mage/Catalog/Model/Resource/Url.php | 201 ++++++++++++++++-- app/code/core/Mage/Catalog/Model/Url.php | 33 ++- app/code/core/Mage/Catalog/etc/config.xml | 1 + app/code/core/Mage/Catalog/etc/system.xml | 9 + 5 files changed, 223 insertions(+), 28 deletions(-) diff --git a/app/code/core/Mage/Catalog/Model/Indexer/Url.php b/app/code/core/Mage/Catalog/Model/Indexer/Url.php index aeda8341a9d..3d1db33adfc 100644 --- a/app/code/core/Mage/Catalog/Model/Indexer/Url.php +++ b/app/code/core/Mage/Catalog/Model/Indexer/Url.php @@ -182,6 +182,7 @@ protected function _registerProductEvent(Mage_Index_Model_Event $event) { $product = $event->getDataObject(); $dataChange = $product->dataHasChangedFor('url_key') + || $product->dataHasChangedFor('status') || $product->getIsChangedCategories() || $product->getIsChangedWebsites(); @@ -199,7 +200,11 @@ protected function _registerCategoryEvent(Mage_Index_Model_Event $event) { $category = $event->getDataObject(); if (!$category->getInitialSetupFlag() && $category->getLevel() > 1) { - if ($category->dataHasChangedFor('url_key') || $category->getIsChangedProductList()) { + if ( + $category->dataHasChangedFor('url_key') + || $category->getIsChangedProductList() + || $category->dataHasChangedFor('is_active') + ) { $event->addNewData('rewrite_category_ids', array($category->getId())); } /** diff --git a/app/code/core/Mage/Catalog/Model/Resource/Url.php b/app/code/core/Mage/Catalog/Model/Resource/Url.php index e7605557728..859cca88752 100644 --- a/app/code/core/Mage/Catalog/Model/Resource/Url.php +++ b/app/code/core/Mage/Catalog/Model/Resource/Url.php @@ -688,9 +688,10 @@ protected function _prepareStoreRootCategories($stores) * @param int|array $categoryIds * @param int $storeId * @param string $path + * @param bool $withDisabled * @return array */ - protected function _getCategories($categoryIds, $storeId = null, $path = null) + protected function _getCategories($categoryIds, $storeId = null, $path = null, $withDisabled = true) { $isActiveAttribute = Mage::getSingleton('eav/config') ->getAttribute(Mage_Catalog_Model_Category::ENTITY, 'is_active'); @@ -738,6 +739,10 @@ protected function _getCategories($categoryIds, $storeId = null, $path = null) $rootCategoryPath = $this->getStores($storeId)->getRootCategoryPath(); $rootCategoryPathLength = strlen($rootCategoryPath); } + if (!$withDisabled) { + $select->where('IF(c.value_id > 0, c.value, d.value) = 1'); + } + $bind = array( 'attribute_id' => (int)$isActiveAttribute->getId(), 'store_id' => (int)$storeId @@ -808,15 +813,16 @@ public function getCategory($categoryId, $storeId) * * @param int|array $categoryIds * @param int $storeId + * @param bool $withDisabled * @return Mage_Catalog_Model_Category[]|false */ - public function getCategories($categoryIds, $storeId) + public function getCategories($categoryIds, $storeId, $withDisabled = true) { if (!$categoryIds || !$storeId) { return false; } - return $this->_getCategories($categoryIds, $storeId); + return $this->_getCategories($categoryIds, $storeId, null, $withDisabled); } /** @@ -939,9 +945,11 @@ public function getProductIdsByCategory($category) * @param int $storeId * @param int $entityId * @param int $lastEntityId + * @param bool $withDisabled * @return array + * @throws Mage_Core_Model_Store_Exception */ - protected function _getProducts($productIds, $storeId, $entityId, &$lastEntityId) + protected function _getProducts($productIds, $storeId, $entityId, &$lastEntityId, $withDisabled = true) { $products = array(); $websiteId = Mage::app()->getStore($storeId)->getWebsiteId(); @@ -969,6 +977,16 @@ protected function _getProducts($productIds, $storeId, $entityId, &$lastEntityId if ($productIds !== null) { $select->where('e.entity_id IN(?)', $productIds); } + if (!$withDisabled) { + $statusId = Mage::getResourceModel('eav/entity_attribute') + ->getIdByCode('catalog_product', 'status'); + $select->join( + ['cpei' => 'catalog_product_entity_int'], + 'cpei.entity_id = e.entity_id AND cpei.entity_type_id = 4 AND cpei.attribute_id = ' . (int)$statusId, + [] + ); + $select->where('cpei.value <> 2'); + } $rowSet = $adapter->fetchAll($select, $bind); foreach ($rowSet as $row) { @@ -1013,12 +1031,14 @@ protected function _getProducts($productIds, $storeId, $entityId, &$lastEntityId * * @param int $productId * @param int $storeId + * @param bool $withDisabled * @return Varien_Object|false + * @throws Mage_Core_Model_Store_Exception */ - public function getProduct($productId, $storeId) + public function getProduct($productId, $storeId, $withDisabled = true) { $entityId = 0; - $products = $this->_getProducts($productId, $storeId, 0, $entityId); + $products = $this->_getProducts($productId, $storeId, 0, $entityId, $withDisabled); if (isset($products[$productId])) { return $products[$productId]; } @@ -1030,11 +1050,12 @@ public function getProduct($productId, $storeId) * * @param int $storeId * @param int $lastEntityId + * @param bool $withDisabled * @return Mage_Catalog_Model_Product[] */ - public function getProductsByStore($storeId, &$lastEntityId) + public function getProductsByStore($storeId, &$lastEntityId, $withDisabled = true) { - return $this->_getProducts(null, $storeId, $lastEntityId, $lastEntityId); + return $this->_getProducts(null, $storeId, $lastEntityId, $lastEntityId, $withDisabled); } /** @@ -1054,32 +1075,87 @@ public function getProductsByCategory(Varien_Object $category, &$lastEntityId) } /** - * Find and remove unused products rewrites - a case when products were moved away from the category - * (either to other category or deleted), so rewrite "category_id-product_id" is invalid + * Unused function. Left for backward compatibility * * @param int $storeId * @return $this */ public function clearCategoryProduct($storeId) + { + return $this->clearRewrites($storeId); + } + + /** + * Find and remove unused rewrites a case when + * - products were moved away from the category (either to other category or deleted), so rewrite "category_id-product_id" is invalid + * - category + * + * @param int $storeId + * @return $this + */ + public function clearRewrites($storeId) { $adapter = $this->_getWriteAdapter(); + list($select, $bind) = $this->getSelectToClearRewrites($adapter, $storeId); - $productUseCategories = Mage::getStoreConfigFlag('catalog/seo/product_use_categories'); + $rewriteIds = $adapter->fetchCol($select, $bind); + if ($rewriteIds) { + $where = array($this->getIdFieldName() . ' IN(?)' => $rewriteIds); + $adapter->delete($this->getMainTable(), $where); + } + + return $this; + } + + /** + * Find and remove unused category rewrites - a case when category is disabled and in config field + * catalog/seo/create_url_for_disabled is set as false + * + * @param int $categoryId + * @param int|null $storeId + * @return $this + */ + public function clearDisabledCategory($categoryId, $storeId = null) + { + if (Mage::getStoreConfigFlag('catalog/seo/create_url_for_disabled')) { + return $this; + } + + if (is_null($storeId)) { + foreach ($this->getStores() as $store) { + $this->clearDisabledCategory($categoryId, $store); + } + return $this; + } + + $adapter = $this->_getWriteAdapter(); + + $table = $this->getTable(array('catalog/category', 'int')); $select = $adapter->select() ->from(array('tur' => $this->getMainTable()), $this->getIdFieldName()) ->joinLeft( - array('tcp' => $this->getTable('catalog/category_product')), - 'tur.category_id = tcp.category_id AND tur.product_id = tcp.product_id', + array('ccei1' => $table), + 'ccei1.attribute_id = :is_active_category_attribute_id AND ccei1.store_id = 0 AND ccei1.entity_id = tur.category_id', + array() + ) + ->joinLeft( + array('ccei2' => $table), + 'ccei2.attribute_id = :is_active_category_attribute_id AND ccei2.store_id = :store_id AND ccei2.entity_id = tur.category_id', array() ) + ->where('IF(ccei2.value_id > 0, ccei2.value, ccei1.value) = 0') ->where('tur.store_id = :store_id') - ->where('tur.category_id IS NOT NULL') - ->where('tur.product_id IS NOT NULL'); + ->where('tur.category_id = :category_id'); - if ($productUseCategories) { - $select->where('tcp.category_id IS NULL'); - } - $rewriteIds = $adapter->fetchCol($select, array('store_id' => $storeId)); + $isActiveCategoryAttribute = Mage::getSingleton('eav/config') + ->getAttribute(Mage_Catalog_Model_Category::ENTITY, 'is_active'); + $bind = [ + 'store_id' => $storeId, + 'is_active_category_attribute_id' => $isActiveCategoryAttribute->getId(), + 'category_id' => $categoryId + ]; + + $rewriteIds = $adapter->fetchCol($select, $bind); if ($rewriteIds) { $where = array($this->getIdFieldName() . ' IN(?)' => $rewriteIds); $adapter->delete($this->getMainTable(), $where); @@ -1088,6 +1164,93 @@ public function clearCategoryProduct($storeId) return $this; } + /** + * Prepare select to find unused rewrites + * as set in configuration fields catalog/seo/product_use_categories and catalog/seo/create_url_for_disabled + * + * @param Magento_Db_Adapter_Pdo_Mysql $adapter + * @param int $storeId + * @return array + */ + protected function getSelectToClearRewrites($adapter, $storeId) + { + $productUseCategories = Mage::getStoreConfigFlag('catalog/seo/product_use_categories'); + $createUrlForDisabled = Mage::getStoreConfigFlag('catalog/seo/create_url_for_disabled'); + + $bind = ['store_id' => $storeId]; + $select = $adapter->select() + ->from(array('tur' => $this->getMainTable()), $this->getIdFieldName()) + ->where('tur.store_id = :store_id') + ->where('tur.is_system = 1'); + + if ($createUrlForDisabled) { + + // Find rewrites for cartegory/product + $select->where('tur.category_id IS NOT NULL') + ->where('tur.product_id IS NOT NULL'); + + // Find unused products rewrites - a case when products were moved away from the category + // (either to other category or deleted), so rewrite "category_id-product_id" is invalid + if ($productUseCategories) { + $select->joinLeft( + array('tcp' => $this->getTable('catalog/category_product')), + 'tur.category_id = tcp.category_id AND tur.product_id = tcp.product_id', + array() + ) + ->where('tcp.category_id IS NULL'); + } + + } else { + // Find products, cartegories and cartegory/product rewrites for disabled products or cartegories + $productTable = $this->getTable(array('catalog/product', 'int')); + $categoryTable = $this->getTable(array('catalog/category', 'int')); + $select->joinLeft( + ['cpei' => $productTable], + 'cpei.entity_id = tur.product_id AND cpei.attribute_id = :product_status_attribute_id', + [] + ) + ->joinLeft( + array('ccei1' => $categoryTable), + 'ccei1.attribute_id = :is_active_category_attribute_id AND ccei1.store_id = 0 AND ccei1.entity_id = tur.category_id', + array() + ) + ->joinLeft( + array('ccei2' => $categoryTable), + 'ccei2.attribute_id = :is_active_category_attribute_id AND ccei2.store_id = :store_id AND ccei2.entity_id = tur.category_id', + array() + ); + $productStatusAttributeId = Mage::getSingleton('eav/config') + ->getAttribute(Mage_Catalog_Model_Product::ENTITY, 'status'); + $bind['product_status_attribute_id'] = $productStatusAttributeId->getId(); + + $isActiveCategoryAttribute = Mage::getSingleton('eav/config') + ->getAttribute(Mage_Catalog_Model_Category::ENTITY, 'is_active'); + $bind['is_active_category_attribute_id'] = $isActiveCategoryAttribute->getId(); + + // product_is_disabled OR (category is disabled) OR (cartegory/product rewrite) + $where = '(cpei.value = 2 OR (IF(ccei2.value_id > 0, ccei2.value, ccei1.value) = 0) OR (tur.category_id IS NOT NULL AND tur.product_id IS NOT NULL) )'; + + // Find products, categories and cartegory/product rewrites for disabled products or categories + if ($productUseCategories) { + $select + ->joinLeft( + array('tcp' => $this->getTable('catalog/category_product')), + 'tcp.category_id = tur.category_id AND tcp.product_id = tur.product_id', + array() + ); + // product_is_disabled OR (category is disabled) OR (cartegory/product rewrite AND product were moved away from the category) + $where ='(cpei.value = 2 OR (IF(ccei2.value_id > 0, ccei2.value, ccei1.value) = 0) OR (tur.category_id IS NOT NULL AND tur.product_id IS NOT NULL AND tcp.category_id IS NULL))'; + } + $select->where($where); + } + $select->group('url_rewrite_id'); + + return [ + $select, + $bind + ]; + } + /** * Remove unused rewrites for product - called after we created all needed rewrites for product and know the categories * where the product is contained ($excludeCategoryIds), so we can remove all invalid product rewrites that have other category ids diff --git a/app/code/core/Mage/Catalog/Model/Url.php b/app/code/core/Mage/Catalog/Model/Url.php index 278b60fae65..e8941ca6a93 100644 --- a/app/code/core/Mage/Catalog/Model/Url.php +++ b/app/code/core/Mage/Catalog/Model/Url.php @@ -116,12 +116,18 @@ class Mage_Catalog_Model_Url */ protected $productUseCategories; + /** + * @var bool + */ + protected $createForDisabled; + /** * Neuca_Base_Model_Url constructor. */ public function __construct() { $this->productUseCategories = Mage::getStoreConfigFlag('catalog/seo/product_use_categories'); + $this->createForDisabled = Mage::getStoreConfigFlag('catalog/seo/create_url_for_disabled'); } /** @@ -267,7 +273,7 @@ public function refreshRewrites($storeId = null) $this->clearStoreInvalidRewrites($storeId); $this->refreshCategoryRewrite($this->getStores($storeId)->getRootCategoryId(), $storeId, false); $this->refreshProductRewrites($storeId); - $this->getResource()->clearCategoryProduct($storeId); + $this->getResource()->clearRewrites($storeId); return $this; } @@ -393,7 +399,7 @@ protected function _refreshProductRewrite(Varien_Object $product, Varien_Object } /** - * Refresh products for catwgory + * Refresh products for category * * @param Varien_Object|Mage_Catalog_Model_Category $category * @return $this @@ -450,7 +456,7 @@ protected function _refreshCategoryProductRewrites(Varien_Object $category) */ public function refreshCategoryRewrite($categoryId, $storeId = null, $refreshProducts = null) { - if (!$refreshProducts) { + if (is_null($refreshProducts)) { $refreshProducts = $this->productUseCategories; } if (is_null($storeId)) { @@ -465,6 +471,11 @@ public function refreshCategoryRewrite($categoryId, $storeId = null, $refreshPro return $this; } + if (!$this->createForDisabled && !$category->getIsActive()) { + $this->getResource()->clearDisabledCategory($category->getId()); + return $this; + } + // Load all childs and refresh all categories $category = $this->getResource()->loadCategoryChilds($category); $categoryIds = array($category->getId()); @@ -497,7 +508,7 @@ public function refreshProductRewrite($productId, $storeId = null) return $this; } - $product = $this->getResource()->getProduct($productId, $storeId); + $product = $this->getResource()->getProduct($productId, $storeId, $this->createForDisabled); if (!$product) { // Product doesn't belong to this store - clear all its url rewrites including root one $this->getResource()->clearProductRewrites($productId, $storeId, array()); @@ -529,6 +540,11 @@ public function refreshProductRewrite($productId, $storeId = null) // Remove all other product rewrites created earlier for this store - they're invalid now $excludeCategoryIds = array_keys($categories); + + // Product is disabled and in configuration set to not create for disabled - clear all its url rewrites including root one + if (!$this->createForDisabled && $product->getStatus() === Mage_Catalog_Model_Product_Status::STATUS_DISABLED) { + $excludeCategoryIds = []; + } $this->getResource()->clearProductRewrites($productId, $storeId, $excludeCategoryIds); unset($categories); @@ -554,7 +570,8 @@ public function refreshProductRewrites($storeId) $process = true; while ($process == true) { - $products = $this->getResource()->getProductsByStore($storeId, $lastEntityId); + $products = $this->getResource()->getProductsByStore($storeId, $lastEntityId, $this->createForDisabled); + if (!$products) { $process = false; break; @@ -573,7 +590,7 @@ public function refreshProductRewrites($storeId) } } if ($loadCategories) { - foreach ($this->getResource()->getCategories($loadCategories, $storeId) as $category) { + foreach ($this->getResource()->getCategories($loadCategories, $storeId, $this->createForDisabled) as $category) { $this->_categories[$category->getId()] = $category; } } @@ -684,8 +701,8 @@ public function getUnusedPathByUrlKey($storeId, $requestPath, $idPath, $urlKey) } // match request_url abcdef1234(-12)(.html) pattern $match = array(); - $regularExpression = '#(?P(.*/)?' . preg_quote($urlKey) . ')(-(?P[0-9]+))?(?P' - . preg_quote($suffix) . ')?$#i'; + $regularExpression = '#(?P(.*/)?' . preg_quote($urlKey, '#') . ')(-(?P[0-9]+))?(?P' + . preg_quote($suffix, '#') . ')?$#i'; if (!preg_match($regularExpression, $requestPath, $match)) { return $this->getUnusedPathByUrlKey($storeId, '-', $idPath, $urlKey); } diff --git a/app/code/core/Mage/Catalog/etc/config.xml b/app/code/core/Mage/Catalog/etc/config.xml index 4f048020615..9f3bbe18105 100644 --- a/app/code/core/Mage/Catalog/etc/config.xml +++ b/app/code/core/Mage/Catalog/etc/config.xml @@ -814,6 +814,7 @@ .html .html 1 + 1 1 - 0 diff --git a/app/code/core/Mage/Catalog/etc/system.xml b/app/code/core/Mage/Catalog/etc/system.xml index 9eac9e58ba6..effde3b852c 100644 --- a/app/code/core/Mage/Catalog/etc/system.xml +++ b/app/code/core/Mage/Catalog/etc/system.xml @@ -291,6 +291,15 @@ 1 1 + + + select + adminhtml/system_config_source_yesno + 4 + 1 + 0 + 1 + select