1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25:
26:
27:
28: 29: 30: 31: 32: 33: 34:
35: class Mage_Catalog_Model_Resource_Category_Flat extends Mage_Index_Model_Resource_Abstract
36: {
37: 38: 39: 40: 41:
42: protected $_storeId = null;
43:
44: 45: 46: 47: 48:
49: protected $_loaded = false;
50:
51: 52: 53: 54: 55:
56: protected $_nodes = array();
57:
58: 59: 60: 61: 62:
63: protected $_columns = null;
64:
65: 66: 67: 68: 69:
70: protected $_columnsSql = null;
71:
72: 73: 74: 75: 76:
77: protected $_attributeCodes = null;
78:
79: 80: 81: 82: 83:
84: protected $_inactiveCategoryIds = null;
85:
86: 87: 88: 89: 90:
91: protected $_isRebuilt = null;
92:
93: 94: 95: 96: 97:
98: protected $_storesRootCategories;
99:
100: 101: 102: 103: 104:
105: protected $_allowTableChanges = true;
106:
107: 108: 109: 110:
111: protected function _construct()
112: {
113: $this->_init('catalog/category_flat', 'entity_id');
114: }
115:
116: 117: 118: 119: 120: 121:
122: public function setStoreId($storeId)
123: {
124: $this->_storeId = (int)$storeId;
125: return $this;
126: }
127:
128: 129: 130: 131: 132:
133: public function getStoreId()
134: {
135: if (is_null($this->_storeId)) {
136: return (int)Mage::app()->getStore()->getId();
137: }
138: return $this->_storeId;
139: }
140:
141: 142: 143: 144: 145:
146: public function getMainTable()
147: {
148: return $this->getMainStoreTable($this->getStoreId());
149: }
150:
151: 152: 153: 154: 155: 156:
157: public function getMainStoreTable($storeId = Mage_Catalog_Model_Abstract::DEFAULT_STORE_ID)
158: {
159: if (is_string($storeId)) {
160: $storeId = intval($storeId);
161: }
162: if ($this->getUseStoreTables() && $storeId) {
163: $suffix = sprintf('store_%d', $storeId);
164: $table = $this->getTable(array('catalog/category_flat', $suffix));
165: } else {
166: $table = parent::getMainTable();
167: }
168:
169: return $table;
170: }
171:
172: 173: 174: 175: 176:
177: public function getUseStoreTables()
178: {
179: return true;
180: }
181:
182: 183: 184: 185: 186: 187:
188: public function addInactiveCategoryIds($ids)
189: {
190: if (!is_array($this->_inactiveCategoryIds)) {
191: $this->_initInactiveCategoryIds();
192: }
193: $this->_inactiveCategoryIds = array_merge($ids, $this->_inactiveCategoryIds);
194: return $this;
195: }
196:
197: 198: 199: 200: 201:
202: protected function _initInactiveCategoryIds()
203: {
204: $this->_inactiveCategoryIds = array();
205: Mage::dispatchEvent('catalog_category_tree_init_inactive_category_ids', array('tree' => $this));
206: return $this;
207: }
208:
209: 210: 211: 212: 213:
214: public function getInactiveCategoryIds()
215: {
216: if (!is_array($this->_inactiveCategoryIds)) {
217: $this->_initInactiveCategoryIds();
218: }
219:
220: return $this->_inactiveCategoryIds;
221: }
222:
223: 224: 225: 226: 227: 228: 229: 230:
231: protected function _loadNodes($parentNode = null, $recursionLevel = 0, $storeId = 0)
232: {
233: $_conn = $this->_getReadAdapter();
234: $startLevel = 1;
235: $parentPath = '';
236: if ($parentNode instanceof Mage_Catalog_Model_Category) {
237: $parentPath = $parentNode->getPath();
238: $startLevel = $parentNode->getLevel();
239: } elseif (is_numeric($parentNode)) {
240: $selectParent = $_conn->select()
241: ->from($this->getMainStoreTable($storeId))
242: ->where('entity_id = ?', $parentNode)
243: ->where('store_id = ?', $storeId);
244: $parentNode = $_conn->fetchRow($selectParent);
245: if ($parentNode) {
246: $parentPath = $parentNode['path'];
247: $startLevel = $parentNode['level'];
248: }
249: }
250: $select = $_conn->select()
251: ->from(
252: array('main_table' => $this->getMainStoreTable($storeId)),
253: array('entity_id',
254: new Zend_Db_Expr('main_table.' . $_conn->quoteIdentifier('name')),
255: new Zend_Db_Expr('main_table.' . $_conn->quoteIdentifier('path')),
256: 'is_active',
257: 'is_anchor'))
258: ->joinLeft(
259: array('url_rewrite'=>$this->getTable('core/url_rewrite')),
260: 'url_rewrite.category_id=main_table.entity_id AND url_rewrite.is_system=1 AND ' .
261: $_conn->quoteInto(
262: 'url_rewrite.product_id IS NULL AND url_rewrite.store_id=? AND ',
263: $storeId) .
264: $_conn->prepareSqlCondition('url_rewrite.id_path', array('like' => 'category/%')),
265: array('request_path' => 'url_rewrite.request_path'))
266: ->where('main_table.is_active = ?', '1')
267: ->where('main_table.include_in_menu = ?', '1')
268: ->order('main_table.position');
269:
270: if ($parentPath) {
271: $select->where($_conn->quoteInto("main_table.path like ?", "$parentPath/%"));
272: }
273: if ($recursionLevel != 0) {
274: $levelField = $_conn->quoteIdentifier('level');
275: $select->where($levelField . ' <= ?', $startLevel + $recursionLevel);
276: }
277:
278: $inactiveCategories = $this->getInactiveCategoryIds();
279:
280: if (!empty($inactiveCategories)) {
281: $select->where('main_table.entity_id NOT IN (?)', $inactiveCategories);
282: }
283:
284:
285: Mage::dispatchEvent('catalog_category_flat_loadnodes_before', array('select' => $select));
286:
287: $arrNodes = $_conn->fetchAll($select);
288: $nodes = array();
289: foreach ($arrNodes as $node) {
290: $node['id'] = $node['entity_id'];
291: $nodes[$node['id']] = Mage::getModel('catalog/category')->setData($node);
292: }
293:
294: return $nodes;
295: }
296:
297: 298: 299: 300: 301: 302: 303:
304: public function addChildNodes($children, $path, $parent)
305: {
306: if (isset($children[$path])) {
307: foreach ($children[$path] as $child) {
308: $childrenNodes = $parent->getChildrenNodes();
309: if ($childrenNodes && isset($childrenNodes[$child->getId()])) {
310: $childrenNodes[$child['entity_id']]->setChildrenNodes(array($child->getId()=>$child));
311: } else {
312: if ($childrenNodes) {
313: $childrenNodes[$child->getId()] = $child;
314: } else {
315: $childrenNodes = array($child->getId()=>$child);
316: }
317: $parent->setChildrenNodes($childrenNodes);
318: }
319:
320: if ($path) {
321: $childrenPath = explode('/', $path);
322: } else {
323: $childrenPath = array();
324: }
325: $childrenPath[] = $child->getId();
326: $childrenPath = implode('/', $childrenPath);
327: $this->addChildNodes($children, $childrenPath, $child);
328: }
329: }
330: }
331:
332: 333: 334: 335: 336: 337: 338: 339:
340: public function getNodes($parentId, $recursionLevel = 0, $storeId = 0)
341: {
342: if (!$this->_loaded) {
343: $selectParent = $this->_getReadAdapter()->select()
344: ->from($this->getMainStoreTable($storeId))
345: ->where('entity_id = ?', $parentId);
346: if ($parentNode = $this->_getReadAdapter()->fetchRow($selectParent)) {
347: $parentNode['id'] = $parentNode['entity_id'];
348: $parentNode = Mage::getModel('catalog/category')->setData($parentNode);
349: $this->_nodes[$parentNode->getId()] = $parentNode;
350: $nodes = $this->_loadNodes($parentNode, $recursionLevel, $storeId);
351: $childrenItems = array();
352: foreach ($nodes as $node) {
353: $pathToParent = explode('/', $node->getPath());
354: array_pop($pathToParent);
355: $pathToParent = implode('/', $pathToParent);
356: $childrenItems[$pathToParent][] = $node;
357: }
358: $this->addChildNodes($childrenItems, $parentNode->getPath(), $parentNode);
359: $childrenNodes = $this->_nodes[$parentNode->getId()];
360: if ($childrenNodes->getChildrenNodes()) {
361: $this->_nodes = $childrenNodes->getChildrenNodes();
362: }
363: else {
364: $this->_nodes = array();
365: }
366: $this->_loaded = true;
367: }
368: }
369: return $this->_nodes;
370: }
371:
372: 373: 374: 375: 376: 377: 378: 379: 380: 381:
382: public function getCategories($parent, $recursionLevel = 0, $sorted = false, $asCollection = false, $toLoad = true)
383: {
384: if ($asCollection) {
385: $select = $this->_getReadAdapter()->select()
386: ->from(array('mt' => $this->getMainStoreTable($this->getStoreId())), array('path'))
387: ->where('mt.entity_id = ?', $parent);
388: $parentPath = $this->_getReadAdapter()->fetchOne($select);
389:
390: $collection = Mage::getModel('catalog/category')->getCollection()
391: ->addNameToResult()
392: ->addUrlRewriteToResult()
393: ->addParentPathFilter($parentPath)
394: ->addStoreFilter()
395: ->addIsActiveFilter()
396: ->addAttributeToFilter('include_in_menu', 1)
397: ->addSortedField($sorted);
398: if ($toLoad) {
399: return $collection->load();
400: }
401: return $collection;
402: }
403: return $this->getNodes($parent, $recursionLevel, Mage::app()->getStore()->getId());
404: }
405:
406: 407: 408: 409: 410: 411: 412:
413: public function getNodeById($nodeId, $nodes = null)
414: {
415: if (is_null($nodes)) {
416: $nodes = $this->getNodes($nodeId);
417: }
418: if (isset($nodes[$nodeId])) {
419: return $nodes[$nodeId];
420: }
421: foreach ($nodes as $node) {
422: if ($node->getChildrenNodes()) {
423: return $this->getNodeById($nodeId, $node->getChildrenNodes());
424: }
425: }
426: return array();
427: }
428:
429: 430: 431: 432: 433:
434: public function isRebuilt()
435: {
436: if ($this->_isRebuilt === null) {
437: $defaultStoreView = Mage::app()->getDefaultStoreView();
438: if ($defaultStoreView === null) {
439: $defaultStoreId = Mage_Core_Model_App::ADMIN_STORE_ID;
440: } else {
441: $defaultStoreId = $defaultStoreView->getId();
442: }
443: $select = $this->_getReadAdapter()->select()
444: ->from($this->getMainStoreTable($defaultStoreId), 'entity_id')
445: ->limit(1);
446: try {
447: $this->_isRebuilt = (bool) $this->_getReadAdapter()->fetchOne($select);
448: } catch (Exception $e) {
449: $this->_isRebuilt = false;
450: }
451: }
452: return $this->_isRebuilt;
453: }
454:
455: 456: 457: 458: 459: 460:
461: public function rebuild($stores = null)
462: {
463: if ($stores === null) {
464: $stores = Mage::app()->getStores();
465: }
466:
467: if (!is_array($stores)) {
468: $stores = array($stores);
469: }
470:
471: $rootId = Mage_Catalog_Model_Category::TREE_ROOT_ID;
472: $categories = array();
473: $categoriesIds = array();
474:
475: foreach ($stores as $store) {
476: if ($this->_allowTableChanges) {
477: $this->_createTable($store->getId());
478: }
479:
480: if (!isset($categories[$store->getRootCategoryId()])) {
481: $select = $this->_getWriteAdapter()->select()
482: ->from($this->getTable('catalog/category'))
483: ->where('path = ?', (string)$rootId)
484: ->orWhere('path = ?', "{$rootId}/{$store->getRootCategoryId()}")
485: ->orWhere('path LIKE ?', "{$rootId}/{$store->getRootCategoryId()}/%");
486: $categories[$store->getRootCategoryId()] = $this->_getWriteAdapter()->fetchAll($select);
487: $categoriesIds[$store->getRootCategoryId()] = array();
488: foreach ($categories[$store->getRootCategoryId()] as $category) {
489: $categoriesIds[$store->getRootCategoryId()][] = $category['entity_id'];
490: }
491: }
492: $categoriesIdsChunks = array_chunk($categoriesIds[$store->getRootCategoryId()], 500);
493: foreach ($categoriesIdsChunks as $categoriesIdsChunk) {
494: $attributesData = $this->_getAttributeValues($categoriesIdsChunk, $store->getId());
495: $data = array();
496: foreach ($categories[$store->getRootCategoryId()] as $category) {
497: if (!isset($attributesData[$category['entity_id']])) {
498: continue;
499: }
500: $category['store_id'] = $store->getId();
501: $data[] = $this->_prepareValuesToInsert(
502: array_merge($category, $attributesData[$category['entity_id']])
503: );
504: }
505: $this->_getWriteAdapter()->insertMultiple($this->getMainStoreTable($store->getId()), $data);
506: }
507: }
508: return $this;
509: }
510:
511: 512: 513: 514: 515: 516:
517: protected function _prepareValuesToInsert($data)
518: {
519: $values = array();
520: foreach (array_keys($this->_columns) as $key => $column) {
521: if (isset($data[$column])) {
522: $values[$column] = $data[$column];
523: } else {
524: $values[$column] = null;
525: }
526: }
527: return $values;
528: }
529:
530: 531: 532: 533: 534: 535:
536: public function createTable($stores)
537: {
538: return $this->_createTable($stores);
539: }
540:
541: 542: 543: 544: 545: 546:
547: protected function _createTable($store)
548: {
549: $tableName = $this->getMainStoreTable($store);
550: $_writeAdapter = $this->_getWriteAdapter();
551: $_writeAdapter->dropTable($tableName);
552: $table = $this->_getWriteAdapter()
553: ->newTable($tableName)
554: ->setComment(sprintf('Catalog Category Flat (Store %d)', $store));
555:
556:
557: if ($this->_columnsSql === null) {
558: $this->_columns = array_merge($this->_getStaticColumns(), $this->_getEavColumns());
559: foreach ($this->_columns as $fieldName => $fieldProp) {
560: $default = $fieldProp['default'];
561: if ($fieldProp['type'][0] == Varien_Db_Ddl_Table::TYPE_TIMESTAMP
562: && $default == 'CURRENT_TIMESTAMP') {
563: $default = Varien_Db_Ddl_Table::TIMESTAMP_INIT;
564: }
565: $table->addColumn($fieldName, $fieldProp['type'][0], $fieldProp['type'][1], array(
566: 'nullable' => $fieldProp['nullable'],
567: 'unsigned' => $fieldProp['unsigned'],
568: 'default' => $default,
569: 'primary' => isset($fieldProp['primary']) ? $fieldProp['primary'] : false,
570: ), ($fieldProp['comment'] != '') ?
571: $fieldProp['comment'] :
572: ucwords(str_replace('_', ' ', $fieldName))
573: );
574: }
575: }
576:
577:
578: $table->addIndex(
579: $_writeAdapter->getIndexName($tableName, array('entity_id')),
580: array('entity_id'),
581: array('type' => 'primary')
582: );
583: $table->addIndex(
584: $_writeAdapter->getIndexName($tableName, array('store_id')), array('store_id'), array('type' => 'index')
585: );
586: $table->addIndex(
587: $_writeAdapter->getIndexName($tableName, array('path')), array('path'), array('type' => 'index')
588: );
589: $table->addIndex(
590: $_writeAdapter->getIndexName($tableName, array('level')), array('level'), array('type' => 'index')
591: );
592:
593:
594: $table->addForeignKey(
595: $_writeAdapter->getForeignKeyName(
596: $tableName, 'entity_id', $this->getTable('catalog/category'), 'entity_id'
597: ),
598: 'entity_id', $this->getTable('catalog/category'), 'entity_id',
599: Varien_Db_Ddl_Table::ACTION_CASCADE, Varien_Db_Ddl_Table::ACTION_CASCADE);
600: $table->addForeignKey(
601: $_writeAdapter->getForeignKeyName($tableName, 'store_id', $this->getTable('core/store'), 'store_id'),
602: 'store_id', $this->getTable('core/store'), 'store_id',
603: Varien_Db_Ddl_Table::ACTION_CASCADE, Varien_Db_Ddl_Table::ACTION_CASCADE);
604: $_writeAdapter->createTable($table);
605: return $this;
606: }
607:
608: 609: 610: 611: 612:
613: protected function _getStaticColumns()
614: {
615: $helper = Mage::getResourceHelper('catalog');
616: $columns = array();
617: $columnsToSkip = array('entity_type_id', 'attribute_set_id');
618: $describe = $this->_getWriteAdapter()->describeTable($this->getTable('catalog/category'));
619:
620: foreach ($describe as $column) {
621: if (in_array($column['COLUMN_NAME'], $columnsToSkip)) {
622: continue;
623: }
624: $_is_unsigned = '';
625: $ddlType = $helper->getDdlTypeByColumnType($column['DATA_TYPE']);
626: $column['DEFAULT'] = trim($column['DEFAULT'],"' ");
627: switch ($ddlType) {
628: case Varien_Db_Ddl_Table::TYPE_SMALLINT:
629: case Varien_Db_Ddl_Table::TYPE_INTEGER:
630: case Varien_Db_Ddl_Table::TYPE_BIGINT:
631: $_is_unsigned = (bool)$column['UNSIGNED'];
632: if ($column['DEFAULT'] === '') {
633: $column['DEFAULT'] = null;
634: }
635:
636: $options = null;
637: if ($column['SCALE'] > 0) {
638: $ddlType = Varien_Db_Ddl_Table::TYPE_DECIMAL;
639: } else {
640: break;
641: }
642: case Varien_Db_Ddl_Table::TYPE_DECIMAL:
643: $options = $column['PRECISION'] . ',' . $column['SCALE'];
644: $_is_unsigned = null;
645: if ($column['DEFAULT'] === '') {
646: $column['DEFAULT'] = null;
647: }
648: break;
649: case Varien_Db_Ddl_Table::TYPE_TEXT:
650: $options = $column['LENGTH'];
651: $_is_unsigned = null;
652: break;
653: case Varien_Db_Ddl_Table::TYPE_TIMESTAMP:
654: $options = null;
655: $_is_unsigned = null;
656: break;
657: case Varien_Db_Ddl_Table::TYPE_DATETIME:
658: $_is_unsigned = null;
659: break;
660:
661: }
662: $columns[$column['COLUMN_NAME']] = array(
663: 'type' => array($ddlType, $options),
664: 'unsigned' => $_is_unsigned,
665: 'nullable' => $column['NULLABLE'],
666: 'default' => ($column['DEFAULT'] === null ? false : $column['DEFAULT']),
667: 'comment' => $column['COLUMN_NAME']
668: );
669: }
670: $columns['store_id'] = array(
671: 'type' => array(Varien_Db_Ddl_Table::TYPE_SMALLINT, 5),
672: 'unsigned' => true,
673: 'nullable' => false,
674: 'default' => '0',
675: 'comment' => 'Store Id'
676: );
677: return $columns;
678: }
679:
680: 681: 682: 683: 684:
685: protected function _getEavColumns()
686: {
687: $columns = array();
688: $attributes = $this->_getAttributes();
689: foreach ($attributes as $attribute) {
690: if ($attribute['backend_type'] == 'static') {
691: continue;
692: }
693: $columns[$attribute['attribute_code']] = array();
694: switch ($attribute['backend_type']) {
695: case 'varchar':
696: $columns[$attribute['attribute_code']] = array(
697: 'type' => array(Varien_Db_Ddl_Table::TYPE_TEXT, 255),
698: 'unsigned' => null,
699: 'nullable' => true,
700: 'default' => null,
701: 'comment' => (string)$attribute['frontend_label']
702: );
703: break;
704: case 'int':
705: $columns[$attribute['attribute_code']] = array(
706: 'type' => array(Varien_Db_Ddl_Table::TYPE_INTEGER, null),
707: 'unsigned' => null,
708: 'nullable' => true,
709: 'default' => null,
710: 'comment' => (string)$attribute['frontend_label']
711: );
712: break;
713: case 'text':
714: $columns[$attribute['attribute_code']] = array(
715: 'type' => array(Varien_Db_Ddl_Table::TYPE_TEXT, '64k'),
716: 'unsigned' => null,
717: 'nullable' => true,
718: 'default' => null,
719: 'comment' => (string)$attribute['frontend_label']
720: );
721: break;
722: case 'datetime':
723: $columns[$attribute['attribute_code']] = array(
724: 'type' => array(Varien_Db_Ddl_Table::TYPE_DATETIME, null),
725: 'unsigned' => null,
726: 'nullable' => true,
727: 'default' => null,
728: 'comment' => (string)$attribute['frontend_label']
729: );
730: break;
731: case 'decimal':
732: $columns[$attribute['attribute_code']] = array(
733: 'type' => array(Varien_Db_Ddl_Table::TYPE_DECIMAL, '12,4'),
734: 'unsigned' => null,
735: 'nullable' => true,
736: 'default' => null,
737: 'comment' => (string)$attribute['frontend_label']
738: );
739: break;
740: }
741: }
742: return $columns;
743: }
744:
745: 746: 747: 748: 749:
750: protected function _getAttributes()
751: {
752: if ($this->_attributeCodes === null) {
753: $select = $this->_getWriteAdapter()->select()
754: ->from($this->getTable('eav/entity_type'), array())
755: ->join(
756: $this->getTable('eav/attribute'),
757: $this->getTable('eav/attribute')
758: . '.entity_type_id = ' . $this->getTable('eav/entity_type') . '.entity_type_id',
759: $this->getTable('eav/attribute').'.*'
760: )
761: ->where(
762: $this->getTable('eav/entity_type') . '.entity_type_code = ?', Mage_Catalog_Model_Category::ENTITY
763: );
764: $this->_attributeCodes = array();
765: foreach ($this->_getWriteAdapter()->fetchAll($select) as $attribute) {
766: $this->_attributeCodes[$attribute['attribute_id']] = $attribute;
767: }
768: }
769: return $this->_attributeCodes;
770: }
771:
772: 773: 774: 775: 776: 777: 778:
779: protected function _getAttributeValues($entityIds, $store_id)
780: {
781: if (!is_array($entityIds)) {
782: $entityIds = array($entityIds);
783: }
784: $values = array();
785:
786: foreach ($entityIds as $entityId) {
787: $values[$entityId] = array();
788: }
789: $attributes = $this->_getAttributes();
790: $attributesType = array(
791: 'varchar',
792: 'int',
793: 'decimal',
794: 'text',
795: 'datetime'
796: );
797: foreach ($attributesType as $type) {
798: foreach ($this->_getAttributeTypeValues($type, $entityIds, $store_id) as $row) {
799: $values[$row['entity_id']][$attributes[$row['attribute_id']]['attribute_code']] = $row['value'];
800: }
801: }
802: return $values;
803: }
804:
805: 806: 807: 808: 809: 810: 811: 812:
813: protected function _getAttributeTypeValues($type, $entityIds, $sid)
814: {
815: $select = $this->_getWriteAdapter()->select()
816: ->from(
817: array('def' => $this->getTable(array('catalog/category', $type))),
818: array('entity_id', 'attribute_id')
819: )
820: ->joinLeft(
821: array('store' => $this->getTable(array('catalog/category', $type))),
822: 'store.entity_id = def.entity_id AND store.attribute_id = def.attribute_id AND store.store_id = '.$sid,
823: array('value' => $this->_getWriteAdapter()->getCheckSql('store.value_id > 0',
824: $this->_getWriteAdapter()->quoteIdentifier('store.value'),
825: $this->_getWriteAdapter()->quoteIdentifier('def.value'))
826: )
827: )
828: ->where('def.entity_id IN (?)', $entityIds)
829: ->where('def.store_id = ?', Mage_Catalog_Model_Abstract::DEFAULT_STORE_ID);
830: return $this->_getWriteAdapter()->fetchAll($select);
831: }
832:
833: 834: 835: 836: 837: 838:
839: public function deleteStores($stores)
840: {
841: $this->_deleteTable($stores);
842: return $this;
843: }
844:
845: 846: 847: 848: 849: 850:
851: protected function _deleteTable($stores)
852: {
853: if (!is_array($stores)) {
854: $stores = array($stores);
855: }
856: foreach ($stores as $store) {
857: $this->_getWriteAdapter()->dropTable($this->getMainStoreTable($store));
858: }
859: return $this;
860: }
861:
862: 863: 864: 865: 866: 867:
868: protected function _synchronize($category)
869: {
870: $table = $this->getMainStoreTable($category->getStoreId());
871: $data = $this->_prepareDataForAllFields($category);
872: $this->_getWriteAdapter()->insertOnDuplicate($table, $data);
873: return $this;
874: }
875:
876: 877: 878: 879: 880: 881: 882:
883: public function synchronize($category = null, $storeIds = array())
884: {
885: if (is_null($category)) {
886: if (empty($storeIds)) {
887: $storeIds = null;
888: }
889: $stores = $this->getStoresRootCategories($storeIds);
890:
891: $storesObjects = array();
892: foreach ($stores as $storeId => $rootCategoryId) {
893: $_store = new Varien_Object(array(
894: 'store_id' => $storeId,
895: 'root_category_id' => $rootCategoryId
896: ));
897: $_store->setIdFieldName('store_id');
898: $storesObjects[] = $_store;
899: }
900:
901: $this->rebuild($storesObjects);
902: } else if ($category instanceof Mage_Catalog_Model_Category) {
903: $categoryId = $category->getId();
904: foreach ($category->getStoreIds() as $storeId) {
905: if ($storeId == Mage_Catalog_Model_Abstract::DEFAULT_STORE_ID) {
906: continue;
907: }
908:
909: $attributeValues = $this->_getAttributeValues($categoryId, $storeId);
910: $data = new Varien_Object($category->getData());
911: $data->addData($attributeValues[$categoryId])
912: ->setStoreId($storeId);
913: $this->_synchronize($data);
914: }
915: } else if (is_numeric($category)) {
916: $write = $this->_getWriteAdapter();
917: $select = $write->select()
918: ->from($this->getTable('catalog/category'))
919: ->where('entity_id=?', $category);
920: $row = $write->fetchRow($select);
921: if (!$row) {
922: return $this;
923: }
924:
925: $stores = $this->getStoresRootCategories();
926: $path = explode('/', $row['path']);
927: foreach ($stores as $storeId => $rootCategoryId) {
928: if (in_array($rootCategoryId, $path)) {
929: $attributeValues = $this->_getAttributeValues($category, $storeId);
930: $data = new Varien_Object($row);
931: $data->addData($attributeValues[$category])
932: ->setStoreId($storeId);
933: $this->_synchronize($data);
934: } else {
935: $where = $write->quoteInto('entity_id = ?', $category);
936: $write->delete($this->getMainStoreTable($storeId), $where);
937: }
938: }
939: }
940:
941: return $this;
942: }
943:
944: 945: 946: 947: 948: 949:
950: public function removeStores($stores)
951: {
952: $this->_deleteTable($stores);
953: return $this;
954: }
955:
956: 957: 958: 959: 960: 961:
962: public function move(array $affectedCategoryIds)
963: {
964: $write = $this->_getWriteAdapter();
965: $select = $write->select()
966: ->from($this->getTable('catalog/category'), array('entity_id', 'path'))
967: ->where('entity_id IN(?)', $affectedCategoryIds);
968: $pairs = $write->fetchPairs($select);
969:
970: $pathCond = array($write->quoteInto('entity_id IN(?)', $affectedCategoryIds));
971: $parentIds = array();
972:
973: foreach ($pairs as $path) {
974: $pathCond[] = $write->quoteInto('path LIKE ?', $path . '/%');
975: $parentIds = array_merge($parentIds, explode('/', $path));
976: }
977:
978: $stores = $this->getStoresRootCategories();
979: $where = join(' OR ', $pathCond);
980: $lastId = 0;
981: while (true) {
982: $select = $write->select()
983: ->from($this->getTable('catalog/category'))
984: ->where('entity_id>?', $lastId)
985: ->where($where)
986: ->order('entity_id')
987: ->limit(500);
988: $rowSet = $write->fetchAll($select);
989:
990: if (!$rowSet) {
991: break;
992: }
993:
994: $addStores = array();
995: $remStores = array();
996:
997: foreach ($rowSet as &$row) {
998: $lastId = $row['entity_id'];
999: $path = explode('/', $row['path']);
1000: foreach ($stores as $storeId => $rootCategoryId) {
1001: if (in_array($rootCategoryId, $path)) {
1002: $addStores[$storeId][$row['entity_id']] = $row;
1003: } else {
1004: $remStores[$storeId][] = $row['entity_id'];
1005: }
1006: }
1007: }
1008:
1009:
1010: foreach ($remStores as $storeId => $categoryIds) {
1011: $where = $write->quoteInto('entity_id IN(?)', $categoryIds);
1012: $write->delete($this->getMainStoreTable($storeId), $where);
1013: }
1014:
1015:
1016: foreach ($addStores as $storeId => $storeCategoryIds) {
1017: $attributeValues = $this->_getAttributeValues(array_keys($storeCategoryIds), $storeId);
1018: foreach ($storeCategoryIds as $row) {
1019: $data = new Varien_Object($row);
1020: $data->addData($attributeValues[$row['entity_id']])
1021: ->setStoreId($storeId);
1022: $this->_synchronize($data);
1023: }
1024: }
1025: }
1026:
1027: return $this;
1028: }
1029:
1030: 1031: 1032: 1033: 1034: 1035: 1036: 1037:
1038: public function moveold($categoryId, $prevParentId, $parentId)
1039: {
1040: $catalogCategoryTable = $this->getTable('catalog/category');
1041: $_staticFields = array(
1042: 'parent_id',
1043: 'path',
1044: 'level',
1045: 'position',
1046: 'children_count',
1047: 'updated_at'
1048: );
1049: $prevParent = Mage::getModel('catalog/category')->load($prevParentId);
1050: $parent = Mage::getModel('catalog/category')->load($parentId);
1051: if ($prevParent->getStore()->getWebsiteId() != $parent->getStore()->getWebsiteId()) {
1052: foreach ($prevParent->getStoreIds() as $storeId) {
1053: $this->_getWriteAdapter()->delete(
1054: $this->getMainStoreTable($storeId),
1055: $this->_getWriteAdapter()->quoteInto('entity_id = ?', $categoryId)
1056: );
1057: }
1058: $select = $this->_getReadAdapter()->select()
1059: ->from($catalogCategoryTable, 'path')
1060: ->where('entity_id = ?', $categoryId);
1061:
1062: $categoryPath = $this->_getWriteAdapter()->fetchOne($select);
1063:
1064: $select = $this->_getWriteAdapter()->select()
1065: ->from($catalogCategoryTable, 'entity_id')
1066: ->where('path LIKE ?', "$categoryPath/%")
1067: ->orWhere('path = ?', $categoryPath);
1068: $_categories = $this->_getWriteAdapter()->fetchAll($select);
1069: foreach ($_categories as $_category) {
1070: foreach ($parent->getStoreIds() as $storeId) {
1071: $_tmpCategory = Mage::getModel('catalog/category')
1072: ->setStoreId($storeId)
1073: ->load($_category['entity_id']);
1074: $this->_synchronize($_tmpCategory);
1075: }
1076: }
1077: } else {
1078: foreach ($parent->getStoreIds() as $store) {
1079: $mainStoreTable = $this->getMainStoreTable($store);
1080:
1081: $update = "UPDATE {$mainStoreTable}, {$catalogCategoryTable} SET";
1082: foreach ($_staticFields as $field) {
1083: $update .= " {$mainStoreTable}.".$field."={$catalogCategoryTable}.".$field.",";
1084: }
1085: $update = substr($update, 0, -1);
1086: $update .= " WHERE {$mainStoreTable}.entity_id = {$catalogCategoryTable}.entity_id AND " .
1087: "($catalogCategoryTable}.path like '{$parent->getPath()}/%' OR " .
1088: "{$catalogCategoryTable}.path like '{$prevParent->getPath()}/%')";
1089: $this->_getWriteAdapter()->query($update);
1090: }
1091: }
1092: $prevParent = null;
1093: $parent = null;
1094: $_tmpCategory = null;
1095:
1096: return $this;
1097: }
1098:
1099: 1100: 1101: 1102: 1103: 1104: 1105: 1106: 1107: 1108:
1109: protected function _prepareDataForAllFields($category, $replaceFields = array())
1110: {
1111: $table = $this->getMainStoreTable($category->getStoreId());
1112: $this->_getWriteAdapter()->resetDdlCache($table);
1113: $table = $this->_getWriteAdapter()->describeTable($table);
1114: $data = array();
1115: $idFieldName = Mage::getSingleton('catalog/category')->getIdFieldName();
1116: foreach ($table as $column => $columnData) {
1117: if ($column != $idFieldName || null !== $category->getData($column)) {
1118: if (key_exists($column, $replaceFields)) {
1119: $value = $category->getData($replaceFields[$column]);
1120: } else {
1121: $value = $category->getData($column);
1122: }
1123: if (is_array($value)) {
1124: $value = implode(',', $value);
1125: }
1126: $data[$column] = $value;
1127: }
1128: }
1129: return $data;
1130: }
1131:
1132: 1133: 1134: 1135: 1136: 1137: 1138:
1139: public function getAttribute($attribute)
1140: {
1141: return Mage::getSingleton('catalog/config')
1142: ->getAttribute(Mage_Catalog_Model_Category::ENTITY, $attribute);
1143: }
1144:
1145: 1146: 1147: 1148: 1149: 1150: 1151:
1152: public function getChildrenAmount($category, $isActiveFlag = true)
1153: {
1154: $_table = $this->getMainStoreTable($category->getStoreId());
1155: $select = $this->_getReadAdapter()->select()
1156: ->from($_table, "COUNT({$_table}.entity_id)")
1157: ->where("{$_table}.path LIKE ?", $category->getPath() . '/%')
1158: ->where("{$_table}.is_active = ?", (int) $isActiveFlag);
1159: return (int) $this->_getReadAdapter()->fetchOne($select);
1160: }
1161:
1162: 1163: 1164: 1165: 1166: 1167:
1168: public function getProductCount($category)
1169: {
1170: $select = $this->_getReadAdapter()->select()
1171: ->from(
1172: $this->getTable('catalog/category_product'),
1173: "COUNT({$this->getTable('catalog/category_product')}.product_id)"
1174: )
1175: ->where("{$this->getTable('catalog/category_product')}.category_id = ?", $category->getId())
1176: ->group("{$this->getTable('catalog/category_product')}.category_id");
1177: return (int) $this->_getReadAdapter()->fetchOne($select);
1178: }
1179:
1180: 1181: 1182: 1183: 1184: 1185: 1186:
1187: public function getParentCategories($category, $isActive = true)
1188: {
1189: $categories = array();
1190: $read = $this->_getReadAdapter();
1191: $select = $read->select()
1192: ->from(
1193: array('main_table' => $this->getMainStoreTable($category->getStoreId())),
1194: array('main_table.entity_id', 'main_table.name')
1195: )
1196: ->joinLeft(
1197: array('url_rewrite'=>$this->getTable('core/url_rewrite')),
1198: 'url_rewrite.category_id=main_table.entity_id AND url_rewrite.is_system=1 AND '.
1199: $read->quoteInto('url_rewrite.product_id IS NULL AND url_rewrite.store_id=? AND ',
1200: $category->getStoreId() ).
1201: $read->prepareSqlCondition('url_rewrite.id_path', array('like' => 'category/%')),
1202: array('request_path' => 'url_rewrite.request_path'))
1203: ->where('main_table.entity_id IN (?)', array_reverse(explode(',', $category->getPathInStore())));
1204: if ($isActive) {
1205: $select->where('main_table.is_active = ?', '1');
1206: }
1207: $select->order('main_table.path ASC');
1208: $result = $this->_getReadAdapter()->fetchAll($select);
1209: foreach ($result as $row) {
1210: $row['id'] = $row['entity_id'];
1211: $categories[$row['entity_id']] = Mage::getModel('catalog/category')->setData($row);
1212: }
1213: return $categories;
1214: }
1215:
1216: 1217: 1218: 1219: 1220: 1221:
1222: public function getParentDesignCategory($category)
1223: {
1224: $adapter = $this->_getReadAdapter();
1225: $levelField = $adapter->quoteIdentifier('level');
1226: $pathIds = array_reverse($category->getPathIds());
1227: $select = $adapter->select()
1228: ->from(array('main_table' => $this->getMainStoreTable($category->getStoreId())), '*')
1229: ->where('entity_id IN (?)', $pathIds)
1230: ->where('custom_use_parent_settings = ?', 0)
1231: ->where($levelField . ' != ?', 0)
1232: ->order('level ' . Varien_Db_Select::SQL_DESC);
1233: $result = $adapter->fetchRow($select);
1234: return Mage::getModel('catalog/category')->setData($result);
1235: }
1236:
1237: 1238: 1239: 1240: 1241: 1242:
1243: public function getChildrenCategories($category)
1244: {
1245: $categories = $this->_loadNodes($category, 1, $category->getStoreId());
1246: return $categories;
1247: }
1248:
1249: 1250: 1251: 1252: 1253: 1254:
1255: public function isInRootCategoryList($category)
1256: {
1257: $pathIds = $category->getParentIds();
1258: return in_array(Mage::app()->getStore()->getRootCategoryId(), $pathIds);
1259: }
1260:
1261: 1262: 1263: 1264: 1265: 1266: 1267: 1268:
1269: public function getChildren($category, $recursive = true, $isActive = true)
1270: {
1271: $select = $this->_getReadAdapter()->select()
1272: ->from($this->getMainStoreTable($category->getStoreId()), 'entity_id')
1273: ->where('path LIKE ?', "{$category->getPath()}/%");
1274: if (!$recursive) {
1275: $select->where('level <= ?', $category->getLevel() + 1);
1276: }
1277: if ($isActive) {
1278: $select->where('is_active = ?', '1');
1279: }
1280: $_categories = $this->_getReadAdapter()->fetchAll($select);
1281: $categoriesIds = array();
1282: foreach ($_categories as $_category) {
1283: $categoriesIds[] = $_category['entity_id'];
1284: }
1285: return $categoriesIds;
1286: }
1287:
1288: 1289: 1290: 1291: 1292: 1293:
1294: public function getAllChildren($category)
1295: {
1296: $categoriesIds = $this->getChildren($category);
1297: $myId = array($category->getId());
1298: $categoriesIds = array_merge($myId, $categoriesIds);
1299:
1300: return $categoriesIds;
1301: }
1302:
1303: 1304: 1305: 1306: 1307: 1308:
1309: public function checkId($id)
1310: {
1311: $select = $this->_getReadAdapter()->select()
1312: ->from($this->getMainStoreTable($this->getStoreId()), 'entity_id')
1313: ->where('entity_id=?', $id);
1314: return $this->_getReadAdapter()->fetchOne($select);
1315: }
1316:
1317: 1318: 1319: 1320: 1321: 1322:
1323: public function getDesignUpdateData($category)
1324: {
1325: $categories = array();
1326: $pathIds = array();
1327: foreach (array_reverse($category->getParentIds()) as $pathId) {
1328: if ($pathId == Mage::app()->getStore()->getRootCategoryId()) {
1329: $pathIds[] = $pathId;
1330: break;
1331: }
1332: $pathIds[] = $pathId;
1333: }
1334: $select = $this->_getReadAdapter()->select()
1335: ->from(
1336: array('main_table' => $this->getMainStoreTable($category->getStoreId())),
1337: array(
1338: 'main_table.entity_id',
1339: 'main_table.custom_design',
1340: 'main_table.custom_design_apply',
1341: 'main_table.custom_design_from',
1342: 'main_table.custom_design_to',
1343: )
1344: )
1345: ->where('main_table.entity_id IN (?)', $pathIds)
1346: ->where('main_table.is_active = ?', '1')
1347: ->order('main_table.path ' . Varien_Db_Select::SQL_DESC);
1348: $result = $this->_getReadAdapter()->fetchAll($select);
1349: foreach ($result as $row) {
1350: $row['id'] = $row['entity_id'];
1351: $categories[$row['entity_id']] = Mage::getModel('catalog/category')->setData($row);
1352: }
1353: return $categories;
1354: }
1355:
1356: 1357: 1358: 1359: 1360: 1361: 1362:
1363: public function getAnchorsAbove(array $filterIds, $storeId = 0)
1364: {
1365: $select = $this->_getReadAdapter()->select()
1366: ->from(array('e' => $this->getMainStoreTable($storeId)), 'entity_id')
1367: ->where('is_anchor = ?', 1)
1368: ->where('entity_id IN (?)', $filterIds);
1369:
1370: return $this->_getReadAdapter()->fetchCol($select);
1371: }
1372:
1373: 1374: 1375: 1376: 1377: 1378:
1379: public function getStoresRootCategories($storeIds = null)
1380: {
1381: if (is_null($this->_storesRootCategories)) {
1382: $select = $this->_getWriteAdapter()->select()
1383: ->from(array('cs' => $this->getTable('core/store')), array('store_id'))
1384: ->join(
1385: array('csg' => $this->getTable('core/store_group')),
1386: 'csg.group_id = cs.group_id',
1387: array('root_category_id'))
1388: ->where('cs.store_id <> ?', Mage_Catalog_Model_Abstract::DEFAULT_STORE_ID);
1389: $this->_storesRootCategories = $this->_getWriteAdapter()->fetchPairs($select);
1390: }
1391:
1392: if (!is_null($storeIds)) {
1393: if (!is_array($storeIds)) {
1394: $storeIds = array($storeIds);
1395: }
1396:
1397: $stores = array();
1398: foreach ($this->_storesRootCategories as $storeId => $rootId) {
1399: if (in_array($storeId, $storeIds)) {
1400: $stores[$storeId] = $rootId;
1401: }
1402: }
1403: return $stores;
1404: }
1405:
1406: return $this->_storesRootCategories;
1407: }
1408:
1409: 1410: 1411: 1412: 1413:
1414: protected function _createTables()
1415: {
1416: if ($this->_allowTableChanges) {
1417: foreach (Mage::app()->getStores() as $store) {
1418: $this->_createTable($store->getId());
1419: }
1420: }
1421: return $this;
1422: }
1423:
1424: 1425: 1426: 1427: 1428:
1429: public function reindexAll()
1430: {
1431: $this->_createTables();
1432: $allowTableChanges = $this->_allowTableChanges;
1433: if ($allowTableChanges) {
1434: $this->_allowTableChanges = false;
1435: }
1436: $this->beginTransaction();
1437: try {
1438: $this->rebuild();
1439: $this->commit();
1440: if ($allowTableChanges) {
1441: $this->_allowTableChanges = true;
1442: }
1443: } catch (Exception $e) {
1444: $this->rollBack();
1445: if ($allowTableChanges) {
1446: $this->_allowTableChanges = true;
1447: }
1448: throw $e;
1449: }
1450: return $this;
1451: }
1452: }
1453: