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