Индексирование в Magento

5,348 views

Published on

Published in: Technology
0 Comments
6 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
5,348
On SlideShare
0
From Embeds
0
Number of Embeds
1,648
Actions
Shares
0
Downloads
25
Comments
0
Likes
6
Embeds 0
No embeds

No notes for slide

Индексирование в Magento

  1. 1. Индексация в Magento<br />Виктор Тихончук<br />Magento System Architect<br />
  2. 2. Модуль Mage_Index<br />События<br />Индексатор<br />Индекс<br />Процесс<br />
  3. 3. Событие<br />Mage_Index_Model_Event<br />Событие – то, что происходит в некоторый момент времени и рассматривается как изменение состояния сущности.<br />Событие хранит информацию о сущности, с которой произошло действие и тип действия.<br />Типы изменений сущности (event type)<br />Сохранение (SAVE)<br />Удаление (DELETE)<br />Групповая обработка (MASS_ACTION)<br />
  4. 4. Обработчик<br />Mage_Index_Model_Indexer<br />processEntityAction($entity, $entityType, $eventType)<br />logEvent($entity, $entityType, $eventType)<br />registerEvent(Mage_Index_Model_Event $event)<br />indexEvent(Mage_Index_Model_Event $event)<br />indexEvents($entity = null, $type = null)<br />
  5. 5. Изменения в абстрактной модели<br />Процесс сохранения<br />_getResource()->beginTransaction()<br />_beforeSave()<br />_getResource()->save()<br />_afterSave()<br />_getResource()->commit()<br />afterCommitCallback()<br />Новые события<br />model_save_commit_after<br />{eventPrefix}_save_commit_after<br />
  6. 6. Изменения в абстрактной модели<br />Процесс удаления<br />_getResource()->beginTransaction()<br />_beforeDelete()<br />_getResource()->delete()<br />_afterDelete()<br />_getResource()->commit()<br />_afterDeleteCommit()<br />Новые события<br />model_delete_commit_after<br />{eventPrefix}_delete_commit_after<br />
  7. 7. Сущности<br />catalog/product (SAVE, DELETE, MASS_ACTION)<br />catalog/category (SAVE, DELETE)<br />catalog/resource_eav_attribute (SAVE, DELETE)<br />customer/group (SAVE)<br />cataloginventory/stock_item (SAVE)<br />tag/tag (SAVE)<br />core/store (SAVE, DELETE)<br />core/store_group (SAVE, DELETE)<br />core/website (SAVE, DELETE)<br />
  8. 8. Процессы и индексаторы<br />Индексатор может работать в 2х режимах:<br />в режиме реального времени (MODE_REAL_TIME)<br />в ручном режиме (MODE_MANUAL)<br />У каждого индексатора есть текущий статус:<br />работает (STATUS_RUNNING)<br />режим ожидания (STATUS_PENDING)<br />необходимо перестроить (STATUS_REQUIRE_REINDEX)<br />Событие<br />REQUIRE_REINDEX<br />MODE MANUAL<br />New Indexer<br />
  9. 9. Процесс<br />Mage_Index_Model_Process<br />matchEvent(Mage_Index_Model_Event $event)<br />register(Mage_Index_Model_Event $event)<br />processEvent(Mage_Index_Model_Event $event)<br />reindexAll()<br />reindexEverything()<br />indexEvents()<br />changeStatus($status)<br />getIndexer()<br />
  10. 10. CatalogInventory Stock Status<br />Цель<br /> оптимизировать затраты на подсчет возможности покупки товара при отображении товаров в каталоге, поиске и т.д.<br />Мотивация<br /> для простых (simple) товаров - динамически определить доступность не является трудозатратной операцией, чего не скажешь о составных (composite), для которых нужно учитывать статус всех его составляющих<br />Задача<br /> сделать предварительно подсчитанный статус для каждого товара с учетом склада и веб сайта<br />
  11. 11. Шаг 1: Структура данных индекса<br />
  12. 12. Шаг 2: Создаем индексатор<br />Mage_CatalogInventory_Model_Indexer_Stock<br />
  13. 13. Шаг 2: Создаем индексатор<br />Mage_Index_Model_Indexer_Abstract<br />Mage_CatalogInventory_Model_Indexer_Stock<br />
  14. 14. Шаг 2: Создаем индексатор<br />Mage_Core_Model_Abstract<br />Mage_Index_Model_Indexer_Abstract<br />Mage_CatalogInventory_Model_Indexer_Stock<br />
  15. 15. Шаг 2: Создаем индексатор<br />Mage_Core_Model_Mysql4_Abstract<br />Mage_Index_Model_Mysql4_Indexer_Abstract<br />Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Indexer_Abstract<br />Mage_CatalogInventory_Model_Mysql4_Indexer_Stock<br />
  16. 16. Шаг 2: Создаем индексатор<br />Абстрактные методы индексатора<br />getName()<br />getDescription()<br />_registerEvent()<br />_processEvent()<br />
  17. 17. Шаг 3: События<br />protected$_matchedEntities = array(<br />    Mage_CatalogInventory_Model_Stock_Item::ENTITY => array(<br />        Mage_Index_Model_Event::TYPE_SAVE<br />    ),<br />    Mage_Catalog_Model_Product::ENTITY => array(<br />        Mage_Index_Model_Event::TYPE_SAVE,<br />        Mage_Index_Model_Event::TYPE_MASS_ACTION,<br />        Mage_Index_Model_Event::TYPE_DELETE<br />    ),<br />    Mage_Core_Model_Store::ENTITY => array(<br />        Mage_Index_Model_Event::TYPE_SAVE<br />    ),<br />    Mage_Core_Model_Store_Group::ENTITY => array(<br />        Mage_Index_Model_Event::TYPE_SAVE<br />    ),<br />    Mage_Core_Model_Config_Data::ENTITY => array(<br />        Mage_Index_Model_Event::TYPE_SAVE<br />    ),<br />    Mage_Catalog_Model_Convert_Adapter_Product::ENTITY => array(<br />        Mage_Index_Model_Event::TYPE_SAVE<br />    )<br />);<br />
  18. 18. Шаг 4: Изменение конфигурации<br />protected$_relatedConfigSettings = array(    Mage_CatalogInventory_Model_Stock_Item::XML_PATH_MANAGE_STOCK,    Mage_CatalogInventory_Helper_Data::XML_PATH_SHOW_OUT_OF_STOCK);<br />public functionmatchEvent(Mage_Index_Model_Event$event)<br />{<br />// check saved result in $event and return it<br /> if ($entity == Mage_Core_Model_Config_Data::ENTITY) {<br />$configData= $event->getDataObject();<br />$path = $configData->getPath();<br />if (in_array($path, $this->_relatedConfigSettings)) {<br />$result = $configData->isValueChanged();<br /> } else {<br />$result = false;<br /> }<br /> } else {<br />$result = parent::matchEvent($event);<br /> }// save result in $event and return it<br />}<br />
  19. 19. Шаг 5: Регистрация события<br />protected function _registerEvent(Mage_Index_Model_Event$event)<br />{<br />    switch ($event->getEntity()) {<br />        caseMage_Catalog_Model_Product::ENTITY:<br />            $this->_registerCatalogProductEvent($event);<br />            break;<br />  // skip some cases<br />        caseMage_Core_Model_Store::ENTITY:<br />        caseMage_Core_Model_Config_Data::ENTITY:<br />$reindex = Mage_Index_Model_Process::STATUS_REQUIRE_REINDEX;<br />            $event->addNewData('skip_call_event_handler', true);<br />            $process = $event->getProcess();<br />            $process->changeStatus($reindex);<br /> <br />            if ($event->getEntity() == Mage_Core_Model_Config_Data::ENTITY) {<br />                $configData = $event->getDataObject();<br />$indexer = Mage::getSingleton('index/indexer');<br />                if ($configData->getPath() == XML_PATH_SHOW_OUT_OF_STOCK) {<br />                    $index->getProcessByCode('catalog_product_price')<br />                        ->changeStatus($reindex);<br />                    $indexer->getProcessByCode('catalog_product_attribute')<br />                        ->changeStatus($reindex);<br />                }<br />            }<br />            break;<br />    }<br />}<br />
  20. 20. Шаг 5: Регистрация события<br />protected function _registerCatalogProductDeleteEvent<br />(Mage_Index_Model_Event$event)<br />{<br />    /**@var $product Mage_Catalog_Model_Product */<br />    $product = $event->getDataObject();<br /> <br />    $parentIds = $this->_getResource()<br />->getProductParentsByChild($product->getId());<br />    if ($parentIds) {<br />        $event->addNewData('reindex_stock_parent_ids', $parentIds);<br />    }<br /> <br />    return$this;<br />}<br />
  21. 21. Шаг 6: Обработка события<br />protected function _processEvent(Mage_Index_Model_Event$event)<br />{<br />$data = $event->getNewData();<br />if (!empty($data['cataloginventory_stock_reindex_all'])) {<br />$this->reindexAll();<br /> }<br />if (empty($data['skip_call_event_handler'])) {<br />$this->callEventHandler($event);<br /> }<br />}<br />
  22. 22. Шаг 7: Ресурс модель<br />Resource Model<br />Type Indexer Interface<br />Type Indexer Default<br />Type Indexer Configurable<br />
  23. 23. Шаг 7: Ресурс модель<br />protected function _getTypeIndexers()<br /> {<br />if (is_null($this->_indexers)) {<br />$this->_indexers = array();<br />$types = Mage::getSingleton('catalog/product_type')<br /> ->getTypesByPriority();<br />foreach ($typesas$typeId => $typeInfo) {<br />if (isset($typeInfo['stock_indexer'])) {<br />$modelName = $typeInfo['stock_indexer'];<br /> } else {<br />$modelName = $this->_defaultIndexer;<br /> }<br />$isComposite = !empty($typeInfo['composite']);<br />$indexer = Mage::getResourceModel($modelName)<br /> ->setTypeId($typeId)<br /> ->setIsComposite($isComposite);<br />$this->_indexers[$typeId] = $indexer;<br /> }<br /> }<br />return$this->_indexers;<br /> }<br />
  24. 24. Шаг 7: Ресурс модель<br />public functionreindexAll()<br /> {<br />$this->useIdxTable(true);<br />$this->clearTemporaryIndexTable();<br />foreach ($this->_getTypeIndexers() as$indexer) {<br />$indexer->reindexAll();<br /> }<br />$this->syncData();<br />return$this;<br /> }<br />
  25. 25. Шаг 7: Ресурс модель<br />public functioncatalogProductDelete(Mage_Index_Model_Event$event)<br /> {<br />$data = $event->getNewData();<br />if (empty($data['reindex_stock_parent_ids'])) {<br />return$this;<br /> }<br />$adapter = $this->_getWriteAdapter();<br />$parentIds = array();<br />foreach ($data['reindex_stock_parent_ids'] as$parentId => $parentType) {<br />$parentIds[$parentType][$parentId] = $parentId;<br /> }<br />$adapter->beginTransaction();<br />try {<br />foreach ($parentIdsas$parentType => $entityIds) {<br />$this->_getIndexer($parentType)->reindexEntity($entityIds);<br /> }<br /> } catch (Exception $e) {<br />$adapter->rollback();<br />throw$e;<br /> }<br />$adapter->commit();<br />return$this;<br /> }<br />
  26. 26. Шаг 8: Ресурс модели (TYPE Default)<br />public functionsetTypeId($typeId)<br /> {<br />$this->_typeId = $typeId;<br />return $this;<br /> }<br />public function getTypeId()<br /> {<br />if (is_null($this->_typeId)) {<br /> Mage::throwException(Mage::helper('cataloginventory')<br />->__('Undefined product type.'));<br /> }<br />return$this->_typeId;<br /> }<br />public function reindexAll()<br /> {<br />$this->useIdxTable(true);<br />$this->_prepareIndexTable();<br />return$this;<br /> }<br />public function reindexEntity($entityIds)<br /> {<br />$this->_updateIndex($entityIds);<br />return$this;<br /> }<br />
  27. 27. Шаг 9: Декларация индексатора<br /><config><br /><!-- ... --><br /><global><br /><!-- ... --><br /><index><br /> <indexer><br /><cataloginventory_stock><br /> <model>cataloginventory/indexer_stock</model><br /> </cataloginventory_stock><br /> <catalog_product_attribute><br /> <depends><br /> <cataloginventory_stock /><br /> </depends><br /> </catalog_product_attribute><br /> <catalog_product_price><br /> <depends><br /> <cataloginventory_stock /><br /> </depends><br /> </catalog_product_price><br /> </indexer><br /> </index><br /> </global><br /></config><br />
  28. 28. Индекс цен для B2B<br />Цель<br /> Создать новый тип товара B2B<br />Справка<br /> В2В – сокращение от английских слов «businesstobusiness», в буквальном переводе – бизнес для бизнеса. Это сектор рынка, который работает не на конечного, рядового потребителя, а на такие же компании, то есть на другой бизнес.<br />Задача<br /> Новый тип товара, который наследует поведение простого товара (simple), но имеет возможность указать цену для каждой группы пользователей (customer group)<br />
  29. 29. Индекс цен для B2B<br />Знакомство с интерфейсом Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Indexer_Price_Interface<br />public functionreindexAll();<br />public functionreindexEntity($entityIds);<br />public functionregisterEvent(Mage_Index_Model_Event$event);<br />Создаем свой индексатор<br />class Mageconf_B2b_Model_Mysql4_Indexer_Price<br /> extends<br />Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Indexer_Price_Default<br />{<br />}<br />
  30. 30. Индекс цен для B2B<br />Знакомство с …Indexer_Price_Default<br />public functionreindexAll()<br />{<br />$this->useIdxTable(true);<br />$this->_prepareFinalPriceData();<br />$this->_applyCustomOption();<br />$this->_movePriceDataToIndexTable();<br />return$this;<br />}<br />public functionreindexEntity($entityIds)<br />{<br />$this->_prepareFinalPriceData($entityIds);<br />$this->_applyCustomOption();<br />$this->_movePriceDataToIndexTable();<br />return$this;<br />}<br />
  31. 31. Индекс цен для B2B<br />protected function _getDefaultFinalPriceTable()<br />{<br />if($this->useIdxTable()) {<br />return$this->getTable('catalog/product_price_indexer_final_idx');<br />}<br />return$this->getTable('catalog/product_price_indexer_final_tmp');<br />}<br />
  32. 32. Индекс цен для B2B<br />
  33. 33. Индекс цен для B2B<br />protected function _prepareFinalPriceData($entityIds = null)<br />{<br /> // удаляем данные из таблицы, если они есть<br />$this->_prepareDefaultFinalPriceTable();<br />$write = $this->_getWriteAdapter();<br />$select = $write->select()<br /> ->from(array('e' => $this->getTable('catalog/product')), array('entity_id'))<br /> ->joinCross(<br />array('cg' => $this->getTable('customer/customer_group')),<br />array('customer_group_id'))<br />->joinCross(<br />array('cw' => $this->getTable('core/website')),<br />array('website_id'))<br /> ->join(<br />array('cwd' => $this->_getWebsiteDateTable()),<br />'cw.website_id = cwd.website_id',<br />array())<br /> ->join(<br />array('csg' => $this->getTable('core/store_group')),<br />'csg.website_id = cw.website_id AND cw.default_group_id = csg.group_id',<br />array())<br /> ->join(<br />array('cs' => $this->getTable('core/store')),<br />'csg.default_store_id = cs.store_id AND cs.store_id != 0',<br />array());// next slide<br />
  34. 34. Индекс цен для B2B<br />// protected function _prepareFinalPriceData($entityIds = null)<br /> $select<br /> ->join(<br />array('pw' => $this->getTable('catalog/product_website')),<br />'pw.product_id = e.entity_id AND pw.website_id = cw.website_id',<br />array())<br />->joinLeft(<br />array('tp' => $this->_getTierPriceIndexTable()),<br />'tp.entity_id = e.entity_id AND tp.website_id = cw.website_id'<br />. ' AND tp.customer_group_id = cg.customer_group_id',<br />array())<br /> ->join(<br />array('b2d' => $this->getTable('b2b/price')),<br />'b2d.entity_id = e.entity_id AND b2d.customer IS NULL AND website_id=0'array())<br />->join(<br />array('b2w'=> $this->getTable('b2b/price')),<br />'b2w.entity_id = e.entity_id AND b2w.customer = cg.customer_group_id'<br /> . ' AND b2w.website_id = cw.website_id', array())<br /> ->where('e.type_id=?', $this->getTypeId());<br />$statusCond = $write->quoteInto('=?',<br />Mage_Catalog_Model_Product_Status::STATUS_ENABLED);<br />$this->_addAttributeToSelect($select, 'status', 'e.entity_id', 'cs.store_id', <br />$statusCond, true);<br />
  35. 35. Индекс цен для B2B<br />// protected function _prepareFinalPriceData($entityIds = null)<br />$taxClassId = $this->_addAttributeToSelect($select, 'tax_class_id', 'e.entity_id',<br />'cs.store_id');<br />$select->columns(array('tax_class_id' => $taxClassId));<br />$finalPrice= newZend_Db_Expr('IFNULL(b2w.price, b2d.price)');<br /> $select->columns(array(<br />'orig_price' => $price,<br />'price' => $finalPrice,<br />'min_price' => $finalPrice,<br />'max_price' => $finalPrice,<br />'tier_price' => newZend_Db_Expr('NULL'),<br />'base_tier' => newZend_Db_Expr('NULL'),<br />));<br />if(!is_null($entityIds)) {<br />$select->where('e.entity_id IN(?)', $entityIds);<br /> }<br />
  36. 36. Индекс цен для B2B<br />// protected function _prepareFinalPriceData($entityIds = null)<br />Mage::dispatchEvent('prepare_catalog_product_index_select', array(<br />'select' => $select,<br />'entity_field' => newZend_Db_Expr('e.entity_id'),<br />'website_field' => newZend_Db_Expr('cw.website_id'),<br />'store_field' => newZend_Db_Expr('cs.store_id')<br />));<br />$query = $select->insertromSelect($this->_getDefaultFinalPriceTable());<br />$write->query($query);<br />return$this;<br />}<br />public functionregisterEvent(Mage_Index_Model_Event$event)<br />{<br />$entity = $event->getEntity();<br />if($entity == Mage_Catalog_Model_Product::ENTITY) {<br />if ($event->getType() == Mage_Index_Model_Event::TYPE_SAVE) {<br />// check attributes<br /> // add data to event <br /> }<br /> }<br />}<br />
  37. 37. Обзор индексаторов в Magento<br />Product Attributes<br />Product Prices<br />Catalog URL Rewrites<br />Product Flat Data<br />Category Flat Data<br />Category Products<br />Catalog Search Index<br />Stock Status<br />Tag Aggregation Data<br />
  38. 38. Спасибо за внимание<br />Email: victor@magento.com<br />

×