Magento,Magento Enterprise,ecommerce,электронная коммерция,Google,sitemap

На одном из проектов неожиданно столкнулись с проблемой, при которой sitemap.xml генерировался некорректно.

Суть проблемы заключалась в том, что в конец URL категорий/продуктов добавлялся символ "." в случае, когда URL суффикс категорий/продуктов был пустым.

Кроме того, Magento позволяет задать для каждого Store свой URL суффикс для продуктов/категорий, а при генерации всегда используется дефолтный суффикс.

Баги были замечены в версии Magento Enterprise 1.14.1.0.

Каким образом в Magento Enterprise добавлялся URL суффикс?

В модуле `Enterprise_Catalog` есть эвенты `sitemap_categories_generating_before` и `sitemap_products_generating_before`.

Код:

/**
 * Add Seo suffix to category's URL if doesn't exists.
 *
 * @param Varien_Event_Observer $observer
 */
public function addSeoSuffixToCategoryUrl(Varien_Event_Observer $observer)
{
	$seoSuffix = (string) Mage::app()->getStore()->getConfig(
		Mage_Catalog_Helper_Category::XML_PATH_CATEGORY_URL_SUFFIX
	);
	$this->_addSuffixToUrl($observer->getCollection()->getItems(), $seoSuffix);
}

/**
 * Add Seo suffix to product's URL if doesn't exists.
 *
 * @param Varien_Event_Observer $observer
 */
public function addSeoSuffixToProductUrl(Varien_Event_Observer $observer)
{
	$seoSuffix = (string) Mage::app()->getStore()->getConfig(
		Mage_Catalog_Helper_Product::XML_PATH_PRODUCT_URL_SUFFIX
	);
	$this->_addSuffixToUrl($observer->getCollection()->getItems(), $seoSuffix);
}

/**
 * Iterate via items and add suffix to item's URL.
 *
 * @param $items
 * @param $seoSuffix
 */
protected function _addSuffixToUrl($items, $seoSuffix)
{
	foreach ($items as $item) {
		if ($item->getUrl() && strpos($item->getUrl(), $seoSuffix) === false) {
			$item->setUrl($item->getUrl() . '.' . $seoSuffix);
		}
	}
}

Здесь видно, что нет проверки на пустоту URL суффикса и всегда добавляется точка. Кроме того, всегда берётся суффикс для дефолтного Store.

Этот баг довольно просто пофиксить. Сначала переопределим эвенты. Вместо выполнения эвентов из модуля `Enterprise_Catalog`, выполним свои.

В config.xml в секции adminhtml/events добавляем:


<sitemap_categories_generating_before>
	<observers>
		<set_category_url_suffix>
			<class>turnkeye_catalog/observer</class>
			<method>addSeoSuffixToCategoryUrl</method>
		</set_category_url_suffix>
	</observers>
</sitemap_categories_generating_before>
<sitemap_products_generating_before>
	<observers>
		<set_product_url_suffix>
			<class>turnkeye_catalog/observer</class>
			<method>addSeoSuffixToProductUrl</method>
		</set_product_url_suffix>
	</observers>
</sitemap_products_generating_before>

В обсервере Turnkeye_Catalog_Model_Observer ставим пустые методы:


/**
 * REWRITE DEFAULT ENTERPRISE OBSERVER: DO NOTHING!
 *
 * event: sitemap_categories_generating_before
 *
 * @param Varien_Event_Observer $observer
 * @return $this
 */
public function addSeoSuffixToCategoryUrl(Varien_Event_Observer $observer)
{
	return $this;
}

/**
 * REWRITE DEFAULT ENTERPRISE OBSERVER: DO NOTHING!
 *
 * event: sitemap_products_generating_before
 *
 * @param Varien_Event_Observer $observer
 * @return $this
 */
public function addSeoSuffixToProductUrl(Varien_Event_Observer $observer)
{
	return $this;
}

С этого момента URL суффиксы для категорий/продуктов не будут добавляться.

Теперь добавим их с учётом Store. "Зареврайтим" стандартный класс Mage_Sitemap_Model_Sitemap, в config.xml вашего модуля в секции global/models добавим:

<sitemap>
	<rewrite>
		<sitemap>Turnkeye_Catalog_Rewrite_Mage_Sitemap_Model_Sitemap</sitemap>
	</rewrite>
</sitemap>

Здесь Turnkeye_Catalog_Rewrite_Mage_Sitemap_Model_Sitemap - класс, который наследуется от Mage_Sitemap_Model_Sitemap и переписывает его.

Определим и добавим два метода в наш класс Turnkeye_Catalog_Rewrite_Mage_Sitemap_Model_Sitemap:

protected function _getCategoryUrlSuffix($storeId)
{
	$suffix = (string) Mage::app()->getStore()->getConfig(
		Mage_Catalog_Helper_Category::XML_PATH_CATEGORY_URL_SUFFIX, $storeId
	);
	$suffix = ltrim($suffix, '.');
	if ($suffix) {
		$suffix = '.' . $suffix;
	}

	return $suffix;
}

protected function _getProductUrlSuffix($storeId)
{
	$suffix = (string) Mage::app()->getStore()->getConfig(
		Mage_Catalog_Helper_Product::XML_PATH_PRODUCT_URL_SUFFIX, $storeId
	);
	$suffix = ltrim($suffix, '.');
	if ($suffix) {
		$suffix = '.' . $suffix;
	}

	return $suffix;
}

После этого переопределим метод generateXml в классе Turnkeye_Catalog_Rewrite_Mage_Sitemap_Model_Sitemap следующим образом:


/**
 * Generate XML file
 *
 * @return Mage_Sitemap_Model_Sitemap
 */
public function generateXml()
{	
	$io = new Varien_Io_File();
	$io->setAllowCreateFolders(true);
	$io->open(array('path' => $this->getPath()));

	if ($io->fileExists($this->getSitemapFilename()) && !$io->isWriteable($this->getSitemapFilename())) {
		Mage::throwException(Mage::helper('sitemap')->__('File "%s" cannot be saved. Please, make sure the directory "%s" is writeable by web server.', $this->getSitemapFilename(), $this->getPath()));
	}

	$io->streamOpen($this->getSitemapFilename());

	$io->streamWrite('<?xml version="1.0" encoding="UTF-8"?>' . "\n");
	$io->streamWrite('<urlset xmlns="http: // www. sitemaps.org/schemas/sitemap/0.9">');

	$storeId = $this->getStoreId();
	$date    = Mage::getSingleton('core/date')->gmtDate('Y-m-d');
	$baseUrl = Mage::app()->getStore($storeId)->getBaseUrl(Mage_Core_Model_Store::URL_TYPE_LINK);

	/**
	 * Generate categories sitemap
	 */
	$changefreq = (string)Mage::getStoreConfig('sitemap/category/changefreq', $storeId);
	$priority   = (string)Mage::getStoreConfig('sitemap/category/priority', $storeId);
	$collection = Mage::getResourceModel('sitemap/catalog_category')->getCollection($storeId);
	$categories = new Varien_Object();
	$categories->setItems($collection);
	Mage::dispatchEvent('sitemap_categories_generating_before', array(
		'collection' => $categories
	));
	$suffix = $this->_getCategoryUrlSuffix($storeId);
	foreach ($categories->getItems() as $item) {
		if (mb_strpos($item->getUrl(), 'catalog/category/view/id/', null, 'UTF-8') === false) {
			$item->setUrl($item->getUrl() . $suffix);
		}
		$xml = sprintf(
			'<url><loc>%s</loc><lastmod>%s</lastmod><changefreq>%s</changefreq><priority>%.1f</priority></url>',
			htmlspecialchars($baseUrl . $item->getUrl()),
			$date,
			$changefreq,
			$priority
		);
		$io->streamWrite($xml);
	}
	unset($collection);

	/**
	 * Generate products sitemap
	 */
	$changefreq = (string)Mage::getStoreConfig('sitemap/product/changefreq', $storeId);
	$priority   = (string)Mage::getStoreConfig('sitemap/product/priority', $storeId);
	$collection = Mage::getResourceModel('sitemap/catalog_product')->getCollection($storeId);
	$products = new Varien_Object();
	$products->setItems($collection);
	Mage::dispatchEvent('sitemap_products_generating_before', array(
		'collection' => $products
	));
	$suffix = $this->_getProductUrlSuffix($storeId);
	foreach ($products->getItems() as $item) {
		if (mb_strpos($item->getUrl(), 'catalog/product/view/id/', null, 'UTF-8') === false) {
			$item->setUrl($item->getUrl() . $suffix);
		}
		$xml = sprintf(
			'<url><loc>%s</loc><lastmod>%s</lastmod><changefreq>%s</changefreq><priority>%.1f</priority></url>',
			htmlspecialchars($baseUrl . $item->getUrl()),
			$date,
			$changefreq,
			$priority
		);
		$io->streamWrite($xml);
	}
	unset($collection);

	/**
	 * Generate cms pages sitemap
	 */
	$changefreq = (string)Mage::getStoreConfig('sitemap/page/changefreq', $storeId);
	$priority   = (string)Mage::getStoreConfig('sitemap/page/priority', $storeId);
	$collection = Mage::getResourceModel('sitemap/cms_page')->getCollection($storeId);
	foreach ($collection as $item) {
		$xml = sprintf(
			'<url><loc>%s</loc><lastmod>%s</lastmod><changefreq>%s</changefreq><priority>%.1f</priority></url>',
			htmlspecialchars($baseUrl . $item->getUrl()),
			$date,
			$changefreq,
			$priority
		);
		$io->streamWrite($xml);
	}
	unset($collection);

	$io->streamWrite('</urlset>');
	$io->streamClose();

	$this->setSitemapTime(Mage::getSingleton('core/date')->gmtDate('Y-m-d H:i:s'));
	$this->save();

	return $this;
}

Возможно возникнет вопрос, для чего необходима проверка вида "mb_strpos($item->getUrl(), 'catalog/category/view/id/', null, 'UTF-8')". Для некоторых категорий/продуктов может отсутствовать URL Rewrite и для них будет сгенерирован URL стандартного роутера такого вида. Добавлять к нему суффикс не нужно.

С этого момента в URL продуктов/категорий в sitemap.xml будет генерироваться корректно.