PageRenderTime 58ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

/app/code/core/Mage/Catalog/Model/Url.php

https://bitbucket.org/andrewjleavitt/magestudy
PHP | 888 lines | 507 code | 92 blank | 289 comment | 113 complexity | 8b98bcc412ad170a95cd95186e7ee28b MD5 | raw file
Possible License(s): CC-BY-SA-3.0, LGPL-2.1, GPL-2.0, WTFPL
  1. <?php
  2. /**
  3. * Magento
  4. *
  5. * NOTICE OF LICENSE
  6. *
  7. * This source file is subject to the Open Software License (OSL 3.0)
  8. * that is bundled with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://opensource.org/licenses/osl-3.0.php
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@magentocommerce.com so we can send you a copy immediately.
  14. *
  15. * DISCLAIMER
  16. *
  17. * Do not edit or add to this file if you wish to upgrade Magento to newer
  18. * versions in the future. If you wish to customize Magento for your
  19. * needs please refer to http://www.magentocommerce.com for more information.
  20. *
  21. * @category Mage
  22. * @package Mage_Catalog
  23. * @copyright Copyright (c) 2010 Magento Inc. (http://www.magentocommerce.com)
  24. * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
  25. */
  26. /**
  27. * Catalog url model
  28. *
  29. * @category Mage
  30. * @package Mage_Catalog
  31. * @author Magento Core Team <core@magentocommerce.com>
  32. */
  33. class Mage_Catalog_Model_Url
  34. {
  35. /**
  36. * Number of characters allowed to be in URL path
  37. *
  38. * @var int
  39. */
  40. const MAX_REQUEST_PATH_LENGTH = 240;
  41. /**
  42. * Number of characters allowed to be in URL path
  43. * after MAX_REQUEST_PATH_LENGTH number of characters
  44. *
  45. * @var int
  46. */
  47. const ALLOWED_REQUEST_PATH_OVERFLOW = 10;
  48. /**
  49. * Resource model
  50. *
  51. * @var Mage_Catalog_Model_Resource_Eav_Mysql4_Url
  52. */
  53. protected $_resourceModel;
  54. /**
  55. * Categories cache for products
  56. *
  57. * @var array
  58. */
  59. protected $_categories = array();
  60. /**
  61. * Store root categories cache
  62. *
  63. * @var array
  64. */
  65. protected $_rootCategories = array();
  66. /**
  67. * Rewrite cache
  68. *
  69. * @var array
  70. */
  71. protected $_rewrites = array();
  72. /**
  73. * Current url rewrite rule
  74. *
  75. * @var Varien_Object
  76. */
  77. protected $_rewrite;
  78. /**
  79. * Cache for product rewrite suffix
  80. *
  81. * @var array
  82. */
  83. protected $_productUrlSuffix = array();
  84. /**
  85. * Cache for category rewrite suffix
  86. *
  87. * @var array
  88. */
  89. protected $_categoryUrlSuffix = array();
  90. /**
  91. * Flag to overwrite config settings for Catalog URL rewrites history maintainance
  92. *
  93. * @var bool
  94. */
  95. protected $_saveRewritesHistory = null;
  96. /**
  97. * Singleton of category model for building URL path
  98. *
  99. * @var Mage_Catalog_Model_Category
  100. */
  101. static protected $_categoryForUrlPath;
  102. /**
  103. * Adds url_path property for non-root category - to ensure that url path is not empty.
  104. *
  105. * Sometimes attribute 'url_path' can be empty, because url_path hasn't been generated yet,
  106. * in this case category is loaded with empty url_path and we should generate it manually.
  107. *
  108. * @param Varien_Object $category
  109. * @return void
  110. */
  111. protected function _addCategoryUrlPath($category)
  112. {
  113. if (!($category instanceof Varien_Object) || $category->getUrlPath()) {
  114. return;
  115. }
  116. // This routine is not intended to be used with root categories,
  117. // but handle 'em gracefully - ensure them to have empty path.
  118. if ($category->getLevel() <= 1) {
  119. $category->setUrlPath('');
  120. return;
  121. }
  122. if (self::$_categoryForUrlPath === null) {
  123. self::$_categoryForUrlPath = Mage::getModel('catalog/category');
  124. }
  125. // Generate url_path
  126. $urlPath = self::$_categoryForUrlPath
  127. ->setData($category->getData())
  128. ->getUrlPath();
  129. $category->setUrlPath($urlPath);
  130. }
  131. /**
  132. * Retrieve stores array or store model
  133. *
  134. * @param int $storeId
  135. * @return Mage_Core_Model_Store|array
  136. */
  137. public function getStores($storeId = null)
  138. {
  139. return $this->getResource()->getStores($storeId);
  140. }
  141. /**
  142. * Retrieve resource model
  143. *
  144. * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Url
  145. */
  146. public function getResource()
  147. {
  148. if (is_null($this->_resourceModel)) {
  149. $this->_resourceModel = Mage::getResourceModel('catalog/url');
  150. }
  151. return $this->_resourceModel;
  152. }
  153. /**
  154. * Retrieve Category model singleton
  155. *
  156. * @return Mage_Catalog_Model_Category
  157. */
  158. public function getCategoryModel()
  159. {
  160. return $this->getResource()->getCategoryModel();
  161. }
  162. /**
  163. * Retrieve product model singleton
  164. *
  165. * @return Mage_Catalog_Model_Product
  166. */
  167. public function getProductModel()
  168. {
  169. return $this->getResource()->getProductModel();
  170. }
  171. /**
  172. * Returns store root category, uses caching for it
  173. *
  174. * @return Varien_Object
  175. */
  176. public function getStoreRootCategory($storeId) {
  177. if (!array_key_exists($storeId, $this->_rootCategories)) {
  178. $category = null;
  179. $store = $this->getStores($storeId);
  180. if ($store) {
  181. $rootCategoryId = $store->getRootCategoryId();
  182. $category = $this->getResource()->getCategory($rootCategoryId, $storeId);
  183. }
  184. $this->_rootCategories[$storeId] = $category;
  185. }
  186. return $this->_rootCategories[$storeId];
  187. }
  188. /**
  189. * Setter for $_saveRewritesHistory
  190. * Force Rewrites History save bypass config settings
  191. *
  192. * @return Mage_Catalog_Model_Url
  193. */
  194. public function setShouldSaveRewritesHistory($flag)
  195. {
  196. $this->_saveRewritesHistory = (bool)$flag;
  197. return $this;
  198. }
  199. /**
  200. * Indicate whether to save URL Rewrite History or not (create redirects to old URLs)
  201. *
  202. * @param int $storeId Store View
  203. * @return bool
  204. */
  205. public function getShouldSaveRewritesHistory($storeId = null)
  206. {
  207. if ($this->_saveRewritesHistory !== null) {
  208. return $this->_saveRewritesHistory;
  209. }
  210. return Mage::helper('catalog')->shouldSaveUrlRewritesHistory($storeId);
  211. }
  212. /**
  213. * Refresh all rewrite urls for some store or for all stores
  214. * Used to make full reindexing of url rewrites
  215. *
  216. * @param int $storeId
  217. * @return Mage_Catalog_Model_Url
  218. */
  219. public function refreshRewrites($storeId = null)
  220. {
  221. if (is_null($storeId)) {
  222. foreach ($this->getStores() as $store) {
  223. $this->refreshRewrites($store->getId());
  224. }
  225. return $this;
  226. }
  227. $this->clearStoreInvalidRewrites($storeId);
  228. $this->refreshCategoryRewrite($this->getStores($storeId)->getRootCategoryId(), $storeId, false);
  229. $this->refreshProductRewrites($storeId);
  230. $this->getResource()->clearCategoryProduct($storeId);
  231. return $this;
  232. }
  233. /**
  234. * Refresh category rewrite
  235. *
  236. * @param Varien_Object $category
  237. * @param string $parentPath
  238. * @return Mage_Catalog_Model_Url
  239. */
  240. protected function _refreshCategoryRewrites(Varien_Object $category, $parentPath = null, $refreshProducts = true)
  241. {
  242. if ($category->getId() != $this->getStores($category->getStoreId())->getRootCategoryId()) {
  243. if ($category->getUrlKey() == '') {
  244. $urlKey = $this->getCategoryModel()->formatUrlKey($category->getName());
  245. }
  246. else {
  247. $urlKey = $this->getCategoryModel()->formatUrlKey($category->getUrlKey());
  248. }
  249. $idPath = $this->generatePath('id', null, $category);
  250. $targetPath = $this->generatePath('target', null, $category);
  251. $requestPath = $this->generatePath('request', null, $category, $parentPath);
  252. $rewriteData = array(
  253. 'store_id' => $category->getStoreId(),
  254. 'category_id' => $category->getId(),
  255. 'product_id' => null,
  256. 'id_path' => $idPath,
  257. 'request_path' => $requestPath,
  258. 'target_path' => $targetPath,
  259. 'is_system' => 1
  260. );
  261. $this->getResource()->saveRewrite($rewriteData, $this->_rewrite);
  262. if ($this->getShouldSaveRewritesHistory($category->getStoreId())) {
  263. $this->_saveRewriteHistory($rewriteData, $this->_rewrite);
  264. }
  265. if ($category->getUrlKey() != $urlKey) {
  266. $category->setUrlKey($urlKey);
  267. $this->getResource()->saveCategoryAttribute($category, 'url_key');
  268. }
  269. if ($category->getUrlPath() != $requestPath) {
  270. $category->setUrlPath($requestPath);
  271. $this->getResource()->saveCategoryAttribute($category, 'url_path');
  272. }
  273. }
  274. else {
  275. if ($category->getUrlPath() != '') {
  276. $category->setUrlPath('');
  277. $this->getResource()->saveCategoryAttribute($category, 'url_path');
  278. }
  279. }
  280. if ($refreshProducts) {
  281. $this->_refreshCategoryProductRewrites($category);
  282. }
  283. foreach ($category->getChilds() as $child) {
  284. $this->_refreshCategoryRewrites($child, $category->getUrlPath() . '/', $refreshProducts);
  285. }
  286. return $this;
  287. }
  288. /**
  289. * Refresh product rewrite
  290. *
  291. * @param Varien_Object $product
  292. * @param Varien_Object $category
  293. * @return Mage_Catalog_Model_Url
  294. */
  295. protected function _refreshProductRewrite(Varien_Object $product, Varien_Object $category)
  296. {
  297. if ($category->getId() == $category->getPath()) {
  298. return $this;
  299. }
  300. if ($product->getUrlKey() == '') {
  301. $urlKey = $this->getProductModel()->formatUrlKey($product->getName());
  302. }
  303. else {
  304. $urlKey = $this->getProductModel()->formatUrlKey($product->getUrlKey());
  305. }
  306. $idPath = $this->generatePath('id', $product, $category);
  307. $targetPath = $this->generatePath('target', $product, $category);
  308. $requestPath = $this->getProductRequestPath($product, $category);
  309. $categoryId = null;
  310. $updateKeys = true;
  311. if ($category->getLevel() > 1) {
  312. $categoryId = $category->getId();
  313. $updateKeys = false;
  314. }
  315. $rewriteData = array(
  316. 'store_id' => $category->getStoreId(),
  317. 'category_id' => $categoryId,
  318. 'product_id' => $product->getId(),
  319. 'id_path' => $idPath,
  320. 'request_path' => $requestPath,
  321. 'target_path' => $targetPath,
  322. 'is_system' => 1
  323. );
  324. $this->getResource()->saveRewrite($rewriteData, $this->_rewrite);
  325. if ($this->getShouldSaveRewritesHistory($category->getStoreId())) {
  326. $this->_saveRewriteHistory($rewriteData, $this->_rewrite);
  327. }
  328. if ($updateKeys && $product->getUrlKey() != $urlKey) {
  329. $product->setUrlKey($urlKey);
  330. $this->getResource()->saveProductAttribute($product, 'url_key');
  331. }
  332. if ($updateKeys && $product->getUrlPath() != $requestPath) {
  333. $product->setUrlPath($requestPath);
  334. $this->getResource()->saveProductAttribute($product, 'url_path');
  335. }
  336. return $this;
  337. }
  338. /**
  339. * Refresh products for catwgory
  340. *
  341. * @param Varien_Object $category
  342. * @return Mage_Catalog_Model_Url
  343. */
  344. protected function _refreshCategoryProductRewrites(Varien_Object $category)
  345. {
  346. $originalRewrites = $this->_rewrites;
  347. $process = true;
  348. $lastEntityId = 0;
  349. $firstIteration = true;
  350. while ($process == true) {
  351. $products = $this->getResource()->getProductsByCategory($category, $lastEntityId);
  352. if (!$products) {
  353. if ($firstIteration) {
  354. $this->getResource()->deleteCategoryProductStoreRewrites(
  355. $category->getId(),
  356. array(),
  357. $category->getStoreId()
  358. );
  359. }
  360. $process = false;
  361. break;
  362. }
  363. // Prepare rewrites for generation
  364. $rootCategory = $this->getStoreRootCategory($category->getStoreId());
  365. $categoryIds = array($category->getId(), $rootCategory->getId());
  366. $this->_rewrites = $this->getResource()->prepareRewrites(
  367. $category->getStoreId(),
  368. $categoryIds,
  369. array_keys($products)
  370. );
  371. foreach ($products as $product) {
  372. // Product always must have rewrite in root category
  373. $this->_refreshProductRewrite($product, $rootCategory);
  374. $this->_refreshProductRewrite($product, $category);
  375. }
  376. $firstIteration = false;
  377. unset($products);
  378. }
  379. $this->_rewrites = $originalRewrites;
  380. return $this;
  381. }
  382. /**
  383. * Refresh category and childs rewrites
  384. * Called when reindexing all rewrites and as a reaction on category change that affects rewrites
  385. *
  386. * @param int $categoryId
  387. * @param int|null $storeId
  388. * @param bool $refreshProducts
  389. * @return Mage_Catalog_Model_Url
  390. */
  391. public function refreshCategoryRewrite($categoryId, $storeId = null, $refreshProducts = true)
  392. {
  393. if (is_null($storeId)) {
  394. foreach ($this->getStores() as $store) {
  395. $this->refreshCategoryRewrite($categoryId, $store->getId(), $refreshProducts);
  396. }
  397. return $this;
  398. }
  399. $category = $this->getResource()->getCategory($categoryId, $storeId);
  400. if (!$category) {
  401. return $this;
  402. }
  403. // Load all childs and refresh all categories
  404. $category = $this->getResource()->loadCategoryChilds($category);
  405. $categoryIds = array($category->getId());
  406. if ($category->getAllChilds()) {
  407. $categoryIds = array_merge($categoryIds, array_keys($category->getAllChilds()));
  408. }
  409. $this->_rewrites = $this->getResource()->prepareRewrites($storeId, $categoryIds);
  410. $this->_refreshCategoryRewrites($category, null, $refreshProducts);
  411. unset($category);
  412. $this->_rewrites = array();
  413. return $this;
  414. }
  415. /**
  416. * Refresh product rewrite urls for one store or all stores
  417. * Called as a reaction on product change that affects rewrites
  418. *
  419. * @param int $productId
  420. * @param int|null $storeId
  421. * @return Mage_Catalog_Model_Url
  422. */
  423. public function refreshProductRewrite($productId, $storeId = null)
  424. {
  425. if (is_null($storeId)) {
  426. foreach ($this->getStores() as $store) {
  427. $this->refreshProductRewrite($productId, $store->getId());
  428. }
  429. return $this;
  430. }
  431. $product = $this->getResource()->getProduct($productId, $storeId);
  432. if ($product) {
  433. $store = $this->getStores($storeId);
  434. $storeRootCategoryId = $store->getRootCategoryId();
  435. // List of categories the product is assigned to, filtered by being within the store's categories root
  436. $categories = $this->getResource()->getCategories($product->getCategoryIds(), $storeId);
  437. $this->_rewrites = $this->getResource()->prepareRewrites($storeId, '', $productId);
  438. // Add rewrites for all needed categories
  439. // If product is assigned to any of store's categories -
  440. // we also should use store root category to create root product url rewrite
  441. if (!isset($categories[$storeRootCategoryId])) {
  442. $categories[$storeRootCategoryId] = $this->getResource()->getCategory($storeRootCategoryId, $storeId);
  443. }
  444. // Create product url rewrites
  445. foreach ($categories as $category) {
  446. $this->_refreshProductRewrite($product, $category);
  447. }
  448. // Remove all other product rewrites created earlier for this store - they're invalid now
  449. $excludeCategoryIds = array_keys($categories);
  450. $this->getResource()->clearProductRewrites($productId, $storeId, $excludeCategoryIds);
  451. unset($categories);
  452. unset($product);
  453. } else {
  454. // Product doesn't belong to this store - clear all its url rewrites including root one
  455. $this->getResource()->clearProductRewrites($productId, $storeId, array());
  456. }
  457. return $this;
  458. }
  459. /**
  460. * Refresh all product rewrites for designated store
  461. *
  462. * @param int $storeId
  463. * @return Mage_Catalog_Model_Url
  464. */
  465. public function refreshProductRewrites($storeId)
  466. {
  467. $this->_categories = array();
  468. $storeRootCategoryId = $this->getStores($storeId)->getRootCategoryId();
  469. $storeRootCategoryPath = $this->getStores($storeId)->getRootCategoryPath();
  470. $this->_categories[$storeRootCategoryId] = $this->getResource()->getCategory($storeRootCategoryId, $storeId);
  471. $lastEntityId = 0;
  472. $process = true;
  473. while ($process == true) {
  474. $products = $this->getResource()->getProductsByStore($storeId, $lastEntityId);
  475. if (!$products) {
  476. $process = false;
  477. break;
  478. }
  479. $this->_rewrites = $this->getResource()->prepareRewrites($storeId, false, array_keys($products));
  480. $loadCategories = array();
  481. foreach ($products as $product) {
  482. foreach ($product->getCategoryIds() as $categoryId) {
  483. if (!isset($this->_categories[$categoryId])) {
  484. $loadCategories[$categoryId] = $categoryId;
  485. }
  486. }
  487. }
  488. if ($loadCategories) {
  489. foreach ($this->getResource()->getCategories($loadCategories, $storeId) as $category) {
  490. $this->_categories[$category->getId()] = $category;
  491. }
  492. }
  493. foreach ($products as $product) {
  494. $this->_refreshProductRewrite($product, $this->_categories[$storeRootCategoryId]);
  495. foreach ($product->getCategoryIds() as $categoryId) {
  496. if ($categoryId != $storeRootCategoryId && isset($this->_categories[$categoryId])) {
  497. if (strpos($this->_categories[$categoryId]['path'], $storeRootCategoryPath . '/') !== 0) {
  498. continue;
  499. }
  500. $this->_refreshProductRewrite($product, $this->_categories[$categoryId]);
  501. }
  502. }
  503. }
  504. unset($products);
  505. $this->_rewrites = array();
  506. }
  507. $this->_categories = array();
  508. return $this;
  509. }
  510. /**
  511. * Deletes old rewrites for store, left from the times when store had some other root category
  512. *
  513. * @param int $storeId
  514. * @return Mage_Catalog_Model_Url
  515. */
  516. public function clearStoreInvalidRewrites($storeId = null)
  517. {
  518. if (is_null($storeId)) {
  519. foreach ($this->getStores() as $store) {
  520. $this->clearStoreInvalidRewrites($store->getId());
  521. }
  522. return $this;
  523. }
  524. $this->getResource()->clearStoreInvalidRewrites($storeId);
  525. return $this;
  526. }
  527. /**
  528. * Get requestPath that was not used yet.
  529. *
  530. * Will try to get unique path by adding -1 -2 etc. between url_key and optional url_suffix
  531. *
  532. * @param int $storeId
  533. * @param string $requestPath
  534. * @param string $idPath
  535. * @return string
  536. */
  537. public function getUnusedPath($storeId, $requestPath, $idPath)
  538. {
  539. if (strpos($idPath, 'product') !== false) {
  540. $suffix = $this->getProductUrlSuffix($storeId);
  541. } else {
  542. $suffix = $this->getCategoryUrlSuffix($storeId);
  543. }
  544. if (empty($requestPath)) {
  545. $requestPath = '-';
  546. } elseif ($requestPath == $suffix) {
  547. $requestPath = '-' . $suffix;
  548. }
  549. /**
  550. * Validate maximum length of request path
  551. */
  552. if (strlen($requestPath) > self::MAX_REQUEST_PATH_LENGTH + self::ALLOWED_REQUEST_PATH_OVERFLOW) {
  553. $requestPath = substr($requestPath, 0, self::MAX_REQUEST_PATH_LENGTH);
  554. }
  555. if (isset($this->_rewrites[$idPath])) {
  556. $this->_rewrite = $this->_rewrites[$idPath];
  557. if ($this->_rewrites[$idPath]->getRequestPath() == $requestPath) {
  558. return $requestPath;
  559. }
  560. }
  561. else {
  562. $this->_rewrite = null;
  563. }
  564. $rewrite = $this->getResource()->getRewriteByRequestPath($requestPath, $storeId);
  565. if ($rewrite && $rewrite->getId()) {
  566. if ($rewrite->getIdPath() == $idPath) {
  567. $this->_rewrite = $rewrite;
  568. return $requestPath;
  569. }
  570. // match request_url abcdef1234(-12)(.html) pattern
  571. $match = array();
  572. if (!preg_match('#^([0-9a-z/-]+?)(-([0-9]+))?('.preg_quote($suffix).')?$#i', $requestPath, $match)) {
  573. return $this->getUnusedPath($storeId, '-', $idPath);
  574. }
  575. $requestPath = $match[1].(isset($match[3])?'-'.($match[3]+1):'-1').(isset($match[4])?$match[4]:'');
  576. return $this->getUnusedPath($storeId, $requestPath, $idPath);
  577. }
  578. else {
  579. return $requestPath;
  580. }
  581. }
  582. /**
  583. * Retrieve product rewrite sufix for store
  584. *
  585. * @param int $storeId
  586. * @return string
  587. */
  588. public function getProductUrlSuffix($storeId)
  589. {
  590. return Mage::helper('catalog/product')->getProductUrlSuffix($storeId);
  591. }
  592. /**
  593. * Retrieve category rewrite sufix for store
  594. *
  595. * @param int $storeId
  596. * @return string
  597. */
  598. public function getCategoryUrlSuffix($storeId)
  599. {
  600. return Mage::helper('catalog/category')->getCategoryUrlSuffix($storeId);
  601. }
  602. /**
  603. * Get unique product request path
  604. *
  605. * @param Varien_Object $product
  606. * @param Varien_Object $category
  607. * @return string
  608. */
  609. public function getProductRequestPath($product, $category)
  610. {
  611. if ($product->getUrlKey() == '') {
  612. $urlKey = $this->getProductModel()->formatUrlKey($product->getName());
  613. } else {
  614. $urlKey = $this->getProductModel()->formatUrlKey($product->getUrlKey());
  615. }
  616. $storeId = $category->getStoreId();
  617. $suffix = $this->getProductUrlSuffix($storeId);
  618. $idPath = $this->generatePath('id', $product, $category);
  619. /**
  620. * Prepare product base request path
  621. */
  622. if ($category->getLevel() > 1) {
  623. // To ensure, that category has path either from attribute or generated now
  624. $this->_addCategoryUrlPath($category);
  625. $categoryUrl = Mage::helper('catalog/category')->getCategoryUrlPath($category->getUrlPath(),
  626. false, $storeId);
  627. $requestPath = $categoryUrl . '/' . $urlKey;
  628. } else {
  629. $requestPath = $urlKey;
  630. }
  631. if (strlen($requestPath) > self::MAX_REQUEST_PATH_LENGTH + self::ALLOWED_REQUEST_PATH_OVERFLOW) {
  632. $requestPath = substr($requestPath, 0, self::MAX_REQUEST_PATH_LENGTH);
  633. }
  634. $this->_rewrite = null;
  635. /**
  636. * Check $requestPath should be unique
  637. */
  638. if (isset($this->_rewrites[$idPath])) {
  639. $this->_rewrite = $this->_rewrites[$idPath];
  640. $existingRequestPath = $this->_rewrites[$idPath]->getRequestPath();
  641. $existingRequestPath = str_replace($suffix, '', $existingRequestPath);
  642. if ($existingRequestPath == $requestPath) {
  643. return $requestPath.$suffix;
  644. }
  645. /**
  646. * Check if existing request past can be used
  647. */
  648. if ($product->getUrlKey() == '' && !empty($requestPath)
  649. && strpos($existingRequestPath, $requestPath) !== false
  650. ) {
  651. $existingRequestPath = str_replace($requestPath, '', $existingRequestPath);
  652. if (preg_match('#^-([0-9]+)$#i', $existingRequestPath)) {
  653. return $this->_rewrites[$idPath]->getRequestPath();
  654. }
  655. }
  656. /**
  657. * check if current generated request path is one of the old paths
  658. */
  659. $fullPath = $requestPath.$suffix;
  660. $finalOldTargetPath = $this->getResource()->findFinalTargetPath($fullPath, $storeId);
  661. if ($finalOldTargetPath && $finalOldTargetPath == $idPath) {
  662. $this->getResource()->deleteRewrite($fullPath, $storeId);
  663. return $fullPath;
  664. }
  665. }
  666. /**
  667. * Check 2 variants: $requestPath and $requestPath . '-' . $productId
  668. */
  669. $validatedPath = $this->getResource()->checkRequestPaths(
  670. array($requestPath.$suffix, $requestPath.'-'.$product->getId().$suffix),
  671. $storeId
  672. );
  673. if ($validatedPath) {
  674. return $validatedPath;
  675. }
  676. /**
  677. * Use unique path generator
  678. */
  679. return $this->getUnusedPath($storeId, $requestPath.$suffix, $idPath);
  680. }
  681. /**
  682. * Generate either id path, request path or target path for product and/or category
  683. *
  684. * For generating id or system path, either product or category is required
  685. * For generating request path - category is required
  686. * $parentPath used only for generating category path
  687. *
  688. * @param string $type
  689. * @param Varien_Object $product
  690. * @param Varien_Object $category
  691. * @param string $parentPath
  692. * @return string
  693. * @throws Mage_Core_Exception
  694. */
  695. public function generatePath($type = 'target', $product = null, $category = null, $parentPath = null)
  696. {
  697. if (!$product && !$category) {
  698. Mage::throwException(Mage::helper('core')->__('Please specify either a category or a product, or both.'));
  699. }
  700. // generate id_path
  701. if ('id' === $type) {
  702. if (!$product) {
  703. return 'category/' . $category->getId();
  704. }
  705. if ($category && $category->getLevel() > 1) {
  706. return 'product/' . $product->getId() . '/' . $category->getId();
  707. }
  708. return 'product/' . $product->getId();
  709. }
  710. // generate request_path
  711. if ('request' === $type) {
  712. // for category
  713. if (!$product) {
  714. if ($category->getUrlKey() == '') {
  715. $urlKey = $this->getCategoryModel()->formatUrlKey($category->getName());
  716. }
  717. else {
  718. $urlKey = $this->getCategoryModel()->formatUrlKey($category->getUrlKey());
  719. }
  720. $categoryUrlSuffix = $this->getCategoryUrlSuffix($category->getStoreId());
  721. if (null === $parentPath) {
  722. $parentPath = $this->getResource()->getCategoryParentPath($category);
  723. }
  724. elseif ($parentPath == '/') {
  725. $parentPath = '';
  726. }
  727. $parentPath = Mage::helper('catalog/category')->getCategoryUrlPath($parentPath,
  728. true, $category->getStoreId());
  729. return $this->getUnusedPath($category->getStoreId(), $parentPath . $urlKey . $categoryUrlSuffix,
  730. $this->generatePath('id', null, $category)
  731. );
  732. }
  733. // for product & category
  734. if (!$category) {
  735. Mage::throwException(Mage::helper('core')->__('A category object is required for determining the product request path.')); // why?
  736. }
  737. if ($product->getUrlKey() == '') {
  738. $urlKey = $this->getProductModel()->formatUrlKey($product->getName());
  739. }
  740. else {
  741. $urlKey = $this->getProductModel()->formatUrlKey($product->getUrlKey());
  742. }
  743. $productUrlSuffix = $this->getProductUrlSuffix($category->getStoreId());
  744. if ($category->getLevel() > 1) {
  745. // To ensure, that category has url path either from attribute or generated now
  746. $this->_addCategoryUrlPath($category);
  747. $categoryUrl = Mage::helper('catalog/category')->getCategoryUrlPath($category->getUrlPath(),
  748. false, $category->getStoreId());
  749. return $this->getUnusedPath($category->getStoreId(), $categoryUrl . '/' . $urlKey . $productUrlSuffix,
  750. $this->generatePath('id', $product, $category)
  751. );
  752. }
  753. // for product only
  754. return $this->getUnusedPath($category->getStoreId(), $urlKey . $productUrlSuffix,
  755. $this->generatePath('id', $product)
  756. );
  757. }
  758. // generate target_path
  759. if (!$product) {
  760. return 'catalog/category/view/id/' . $category->getId();
  761. }
  762. if ($category && $category->getLevel() > 1) {
  763. return 'catalog/product/view/id/' . $product->getId() . '/category/' . $category->getId();
  764. }
  765. return 'catalog/product/view/id/' . $product->getId();
  766. }
  767. /**
  768. * Return unique string based on the time in microseconds.
  769. *
  770. * @return string
  771. */
  772. public function generateUniqueIdPath()
  773. {
  774. return str_replace('0.', '', str_replace(' ', '_', microtime()));
  775. }
  776. /**
  777. * Create Custom URL Rewrite for old product/category URL after url_key changed
  778. * It will perform permanent redirect from old URL to new URL
  779. *
  780. * @param array $rewriteData New rewrite data
  781. * @param Varien_Object $rewrite Rewrite model
  782. * @return Mage_Catalog_Model_Url
  783. */
  784. protected function _saveRewriteHistory($rewriteData, $rewrite)
  785. {
  786. if ($rewrite instanceof Varien_Object && $rewrite->getId()) {
  787. $rewriteData['target_path'] = $rewriteData['request_path'];
  788. $rewriteData['request_path'] = $rewrite->getRequestPath();
  789. $rewriteData['id_path'] = $this->generateUniqueIdPath();
  790. $rewriteData['is_system'] = 0;
  791. $rewriteData['options'] = 'RP'; // Redirect = Permanent
  792. $this->getResource()->saveRewriteHistory($rewriteData);
  793. }
  794. return $this;
  795. }
  796. }