Download the patch and put it in your local environment in the root magento directory.
Run this command in your Main magento directory: patch -p0 < fulltext-search.patch
.
Then remove the fulltext-search.patch
file and commit the changes.
Download the patch and put it in your local environment in the root magento directory.
Run this command in your Main magento directory: patch -p0 < fulltext-search.patch
.
Then remove the fulltext-search.patch
file and commit the changes.
From: Jacques Bodin-Hullin <[email protected]> | |
Date: Thu, 13 Oct 2016 16:31:32 +0200 | |
Subject: [PATCH] Fix fulltext search issue after upgrade to Magento 1.9.3.0 | |
--- | |
.../Mage/CatalogSearch/Model/Resource/Fulltext.php | 866 +++++++++++++++++++++ | |
.../Model/Resource/Fulltext/Collection.php | 258 ++++++ | |
2 files changed, 1124 insertions(+) | |
create mode 100644 app/code/local/Mage/CatalogSearch/Model/Resource/Fulltext.php | |
create mode 100644 app/code/local/Mage/CatalogSearch/Model/Resource/Fulltext/Collection.php | |
diff --git a/app/code/local/Mage/CatalogSearch/Model/Resource/Fulltext.php b/app/code/local/Mage/CatalogSearch/Model/Resource/Fulltext.php | |
new file mode 100644 | |
index 0000000..30a1cbd | |
--- /dev/null | |
+++ app/code/local/Mage/CatalogSearch/Model/Resource/Fulltext.php | |
@@ -0,0 +1,866 @@ | |
+<?php | |
+/** | |
+ * Magento | |
+ * | |
+ * NOTICE OF LICENSE | |
+ * | |
+ * This source file is subject to the Open Software License (OSL 3.0) | |
+ * that is bundled with this package in the file LICENSE.txt. | |
+ * It is also available through the world-wide-web at this URL: | |
+ * http://opensource.org/licenses/osl-3.0.php | |
+ * If you did not receive a copy of the license and are unable to | |
+ * obtain it through the world-wide-web, please send an email | |
+ * to [email protected] so we can send you a copy immediately. | |
+ * | |
+ * DISCLAIMER | |
+ * | |
+ * Do not edit or add to this file if you wish to upgrade Magento to newer | |
+ * versions in the future. If you wish to customize Magento for your | |
+ * needs please refer to http://www.magento.com for more information. | |
+ * | |
+ * @category Mage | |
+ * @package Mage_CatalogSearch | |
+ * @copyright Copyright (c) 2006-2016 X.commerce, Inc. and affiliates (http://www.magento.com) | |
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) | |
+ */ | |
+ | |
+ | |
+/** | |
+ * CatalogSearch Fulltext Index resource model | |
+ * | |
+ * @category Mage | |
+ * @package Mage_CatalogSearch | |
+ * @author Magento Core Team <[email protected]> | |
+ */ | |
+class Mage_CatalogSearch_Model_Resource_Fulltext extends Mage_Core_Model_Resource_Db_Abstract | |
+{ | |
+ /** | |
+ * Searchable attributes cache | |
+ * | |
+ * @var array | |
+ */ | |
+ protected $_searchableAttributes = null; | |
+ | |
+ /** | |
+ * Index values separator | |
+ * | |
+ * @var string | |
+ */ | |
+ protected $_separator = '|'; | |
+ | |
+ /** | |
+ * Array of Zend_Date objects per store | |
+ * | |
+ * @var array | |
+ */ | |
+ protected $_dates = array(); | |
+ | |
+ /** | |
+ * Product Type Instances cache | |
+ * | |
+ * @var array | |
+ */ | |
+ protected $_productTypes = array(); | |
+ | |
+ /** | |
+ * Store search engine instance | |
+ * | |
+ * @var object | |
+ */ | |
+ protected $_engine = null; | |
+ | |
+ /** | |
+ * Whether table changes are allowed | |
+ * | |
+ * @deprecated after 1.6.1.0 | |
+ * @var bool | |
+ */ | |
+ protected $_allowTableChanges = true; | |
+ | |
+ /** | |
+ * @var array | |
+ */ | |
+ protected $_foundData = array(); | |
+ | |
+ /** | |
+ * Init resource model | |
+ * | |
+ */ | |
+ protected function _construct() | |
+ { | |
+ $this->_init('catalogsearch/fulltext', 'product_id'); | |
+ $this->_engine = Mage::helper('catalogsearch')->getEngine(); | |
+ } | |
+ | |
+ /** | |
+ * Return options separator | |
+ * | |
+ * @return string | |
+ */ | |
+ public function getSeparator() | |
+ { | |
+ return $this->_separator; | |
+ } | |
+ | |
+ /** | |
+ * Regenerate search index for store(s) | |
+ * | |
+ * @param int|null $storeId | |
+ * @param int|array|null $productIds | |
+ * @return Mage_CatalogSearch_Model_Resource_Fulltext | |
+ */ | |
+ public function rebuildIndex($storeId = null, $productIds = null) | |
+ { | |
+ if (is_null($storeId)) { | |
+ $storeIds = array_keys(Mage::app()->getStores()); | |
+ foreach ($storeIds as $storeId) { | |
+ $this->_rebuildStoreIndex($storeId, $productIds); | |
+ } | |
+ } else { | |
+ $this->_rebuildStoreIndex($storeId, $productIds); | |
+ } | |
+ | |
+ return $this; | |
+ } | |
+ | |
+ /** | |
+ * Regenerate search index for specific store | |
+ * | |
+ * @param int $storeId Store View Id | |
+ * @param int|array $productIds Product Entity Id | |
+ * @return Mage_CatalogSearch_Model_Resource_Fulltext | |
+ */ | |
+ protected function _rebuildStoreIndex($storeId, $productIds = null) | |
+ { | |
+ $this->cleanIndex($storeId, $productIds); | |
+ | |
+ // prepare searchable attributes | |
+ $staticFields = array(); | |
+ foreach ($this->_getSearchableAttributes('static') as $attribute) { | |
+ $staticFields[] = $attribute->getAttributeCode(); | |
+ } | |
+ $dynamicFields = array( | |
+ 'int' => array_keys($this->_getSearchableAttributes('int')), | |
+ 'varchar' => array_keys($this->_getSearchableAttributes('varchar')), | |
+ 'text' => array_keys($this->_getSearchableAttributes('text')), | |
+ 'decimal' => array_keys($this->_getSearchableAttributes('decimal')), | |
+ 'datetime' => array_keys($this->_getSearchableAttributes('datetime')), | |
+ ); | |
+ | |
+ // status and visibility filter | |
+ $visibility = $this->_getSearchableAttribute('visibility'); | |
+ $status = $this->_getSearchableAttribute('status'); | |
+ $statusVals = Mage::getSingleton('catalog/product_status')->getVisibleStatusIds(); | |
+ $allowedVisibilityValues = $this->_engine->getAllowedVisibility(); | |
+ | |
+ $websiteId = Mage::app()->getStore($storeId)->getWebsite()->getId(); | |
+ $lastProductId = 0; | |
+ while (true) { | |
+ $products = $this->_getSearchableProducts($storeId, $staticFields, $productIds, $lastProductId); | |
+ if (!$products) { | |
+ break; | |
+ } | |
+ | |
+ $productAttributes = array(); | |
+ $productRelations = array(); | |
+ foreach ($products as $productData) { | |
+ $lastProductId = $productData['entity_id']; | |
+ $productAttributes[$productData['entity_id']] = $productData['entity_id']; | |
+ $productChildren = $this->_getProductChildrenIds($productData['entity_id'], | |
+ $productData['type_id'], $websiteId); | |
+ $productRelations[$productData['entity_id']] = $productChildren; | |
+ if ($productChildren) { | |
+ foreach ($productChildren as $productChildId) { | |
+ $productAttributes[$productChildId] = $productChildId; | |
+ } | |
+ } | |
+ } | |
+ | |
+ $productIndexes = array(); | |
+ $productAttributes = $this->_getProductAttributes($storeId, $productAttributes, $dynamicFields); | |
+ foreach ($products as $productData) { | |
+ if (!isset($productAttributes[$productData['entity_id']])) { | |
+ continue; | |
+ } | |
+ | |
+ $productAttr = $productAttributes[$productData['entity_id']]; | |
+ if (!isset($productAttr[$visibility->getId()]) | |
+ || !in_array($productAttr[$visibility->getId()], $allowedVisibilityValues) | |
+ ) { | |
+ continue; | |
+ } | |
+ if (!isset($productAttr[$status->getId()]) || !in_array($productAttr[$status->getId()], $statusVals)) { | |
+ continue; | |
+ } | |
+ | |
+ $productIndex = array( | |
+ $productData['entity_id'] => $productAttr | |
+ ); | |
+ | |
+ $hasChildren = false; | |
+ if ($productChildren = $productRelations[$productData['entity_id']]) { | |
+ foreach ($productChildren as $productChildId) { | |
+ if (isset($productAttributes[$productChildId])) { | |
+ $productChildAttr = $productAttributes[$productChildId]; | |
+ if (!isset($productChildAttr[$status->getId()]) | |
+ || !in_array($productChildAttr[$status->getId()], $statusVals) | |
+ ) { | |
+ continue; | |
+ } | |
+ | |
+ $hasChildren = true; | |
+ $productIndex[$productChildId] = $productChildAttr; | |
+ } | |
+ } | |
+ } | |
+ if (!is_null($productChildren) && !$hasChildren) { | |
+ continue; | |
+ } | |
+ | |
+ $index = $this->_prepareProductIndex($productIndex, $productData, $storeId); | |
+ | |
+ $productIndexes[$productData['entity_id']] = $index; | |
+ } | |
+ | |
+ $this->_saveProductIndexes($storeId, $productIndexes); | |
+ } | |
+ | |
+ $this->resetSearchResults(); | |
+ | |
+ return $this; | |
+ } | |
+ | |
+ /** | |
+ * Retrieve searchable products per store | |
+ * | |
+ * @param int $storeId | |
+ * @param array $staticFields | |
+ * @param array|int $productIds | |
+ * @param int $lastProductId | |
+ * @param int $limit | |
+ * @return array | |
+ */ | |
+ protected function _getSearchableProducts($storeId, array $staticFields, $productIds = null, $lastProductId = 0, | |
+ $limit = 100) | |
+ { | |
+ $websiteId = Mage::app()->getStore($storeId)->getWebsiteId(); | |
+ $writeAdapter = $this->_getWriteAdapter(); | |
+ | |
+ $select = $writeAdapter->select() | |
+ ->useStraightJoin(true) | |
+ ->from( | |
+ array('e' => $this->getTable('catalog/product')), | |
+ array_merge(array('entity_id', 'type_id'), $staticFields) | |
+ ) | |
+ ->join( | |
+ array('website' => $this->getTable('catalog/product_website')), | |
+ $writeAdapter->quoteInto( | |
+ 'website.product_id=e.entity_id AND website.website_id=?', | |
+ $websiteId | |
+ ), | |
+ array() | |
+ ) | |
+ ->join( | |
+ array('stock_status' => $this->getTable('cataloginventory/stock_status')), | |
+ $writeAdapter->quoteInto( | |
+ 'stock_status.product_id=e.entity_id AND stock_status.website_id=?', | |
+ $websiteId | |
+ ), | |
+ array('in_stock' => 'stock_status') | |
+ ); | |
+ | |
+ if (!is_null($productIds)) { | |
+ $select->where('e.entity_id IN(?)', $productIds); | |
+ } | |
+ | |
+ $select->where('e.entity_id>?', $lastProductId) | |
+ ->limit($limit) | |
+ ->order('e.entity_id'); | |
+ | |
+ /** | |
+ * Add additional external limitation | |
+ */ | |
+ 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('website.website_id'), | |
+ 'store_field' => $storeId | |
+ )); | |
+ | |
+ $result = $writeAdapter->fetchAll($select); | |
+ | |
+ return $result; | |
+ } | |
+ | |
+ /** | |
+ * Reset search results | |
+ * | |
+ * @return Mage_CatalogSearch_Model_Resource_Fulltext | |
+ */ | |
+ public function resetSearchResults() | |
+ { | |
+ Mage::dispatchEvent('catalogsearch_reset_search_result'); | |
+ return $this; | |
+ } | |
+ | |
+ /** | |
+ * Delete search index data for store | |
+ * | |
+ * @param int $storeId Store View Id | |
+ * @param int $productId Product Entity Id | |
+ * @return Mage_CatalogSearch_Model_Resource_Fulltext | |
+ */ | |
+ public function cleanIndex($storeId = null, $productId = null) | |
+ { | |
+ if ($this->_engine) { | |
+ $this->_engine->cleanIndex($storeId, $productId); | |
+ } | |
+ | |
+ return $this; | |
+ } | |
+ | |
+ /** | |
+ * Prepare results for query | |
+ * | |
+ * @param Mage_CatalogSearch_Model_Fulltext $object | |
+ * @param string $queryText | |
+ * @param Mage_CatalogSearch_Model_Query $query | |
+ * @return Mage_CatalogSearch_Model_Resource_Fulltext | |
+ */ | |
+ public function prepareResult($object, $queryText, $query) | |
+ { | |
+ $adapter = $this->_getWriteAdapter(); | |
+ $searchType = $object->getSearchType($query->getStoreId()); | |
+ | |
+ $preparedTerms = Mage::getResourceHelper('catalogsearch') | |
+ ->prepareTerms($queryText, $query->getMaxQueryWords()); | |
+ | |
+ $bind = array(); | |
+ $like = array(); | |
+ $likeCond = ''; | |
+ | |
+ /** | |
+ * @see http://magento.stackexchange.com/questions/140707/magento-1-9-3-every-product-is-displayed-in-the-search-in-full-text-mode | |
+ */ | |
+ | |
+ if ($searchType == Mage_CatalogSearch_Model_Fulltext::SEARCH_TYPE_LIKE | |
+ || $searchType == Mage_CatalogSearch_Model_Fulltext::SEARCH_TYPE_COMBINE | |
+ ) { | |
+ $helper = Mage::getResourceHelper('core'); | |
+ $words = Mage::helper('core/string')->splitWords($queryText, true, $query->getMaxQueryWords()); | |
+ foreach ($words as $word) { | |
+ $like[] = $helper->getCILike('s.data_index', $word, ['position' => 'any']); | |
+ } | |
+ | |
+ if ($like) { | |
+ $likeCond = '(' . join(' OR ', $like) . ')'; | |
+ } | |
+ } | |
+ | |
+ $mainTableAlias = 's'; | |
+ $fields = array('product_id'); | |
+ | |
+ $select = $adapter->select() | |
+ ->from(array($mainTableAlias => $this->getMainTable()), $fields) | |
+ ->joinInner(array('e' => $this->getTable('catalog/product')), | |
+ 'e.entity_id = s.product_id', | |
+ array()) | |
+ ->where($mainTableAlias . '.store_id = ?', (int)$query->getStoreId()); | |
+ | |
+ $where = ""; | |
+ if ($searchType == Mage_CatalogSearch_Model_Fulltext::SEARCH_TYPE_FULLTEXT | |
+ || $searchType == Mage_CatalogSearch_Model_Fulltext::SEARCH_TYPE_COMBINE | |
+ ) { | |
+ $bind[':query'] = implode(' ', $preparedTerms[0]); | |
+ $where = Mage::getResourceHelper('catalogsearch') | |
+ ->chooseFulltext($this->getMainTable(), $mainTableAlias, $select); | |
+ } | |
+ if ($likeCond != '' && $searchType == Mage_CatalogSearch_Model_Fulltext::SEARCH_TYPE_COMBINE) { | |
+ $where .= ($where ? ' OR ' : '') . $likeCond; | |
+ } elseif ($likeCond != '' && $searchType == Mage_CatalogSearch_Model_Fulltext::SEARCH_TYPE_LIKE) { | |
+ $select->columns(array('relevance' => new Zend_Db_Expr(0))); | |
+ $where = $likeCond; | |
+ } | |
+ | |
+ if ($where != '') { | |
+ $select->where($where); | |
+ } | |
+ | |
+ $this->_foundData = $adapter->fetchPairs($select, $bind); | |
+ | |
+ return $this; | |
+ } | |
+ | |
+ /** | |
+ * Retrieve found data | |
+ * | |
+ * @return array | |
+ */ | |
+ public function getFoundData() | |
+ { | |
+ return $this->_foundData; | |
+ } | |
+ | |
+ /** | |
+ * Retrieve EAV Config Singleton | |
+ * | |
+ * @return Mage_Eav_Model_Config | |
+ */ | |
+ public function getEavConfig() | |
+ { | |
+ return Mage::getSingleton('eav/config'); | |
+ } | |
+ | |
+ /** | |
+ * Retrieve searchable attributes | |
+ * | |
+ * @param string $backendType | |
+ * @return array | |
+ */ | |
+ protected function _getSearchableAttributes($backendType = null) | |
+ { | |
+ if (is_null($this->_searchableAttributes)) { | |
+ $this->_searchableAttributes = array(); | |
+ | |
+ $productAttributeCollection = Mage::getResourceModel('catalog/product_attribute_collection'); | |
+ | |
+ if ($this->_engine && $this->_engine->allowAdvancedIndex()) { | |
+ $productAttributeCollection->addToIndexFilter(true); | |
+ } else { | |
+ $productAttributeCollection->addSearchableAttributeFilter(); | |
+ } | |
+ $attributes = $productAttributeCollection->getItems(); | |
+ | |
+ Mage::dispatchEvent('catalogsearch_searchable_attributes_load_after', array( | |
+ 'engine' => $this->_engine, | |
+ 'attributes' => $attributes | |
+ )); | |
+ | |
+ $entity = $this->getEavConfig() | |
+ ->getEntityType(Mage_Catalog_Model_Product::ENTITY) | |
+ ->getEntity(); | |
+ | |
+ foreach ($attributes as $attribute) { | |
+ $attribute->setEntity($entity); | |
+ } | |
+ | |
+ $this->_searchableAttributes = $attributes; | |
+ } | |
+ | |
+ if (!is_null($backendType)) { | |
+ $attributes = array(); | |
+ foreach ($this->_searchableAttributes as $attributeId => $attribute) { | |
+ if ($attribute->getBackendType() == $backendType) { | |
+ $attributes[$attributeId] = $attribute; | |
+ } | |
+ } | |
+ | |
+ return $attributes; | |
+ } | |
+ | |
+ return $this->_searchableAttributes; | |
+ } | |
+ | |
+ /** | |
+ * Retrieve searchable attribute by Id or code | |
+ * | |
+ * @param int|string $attribute | |
+ * @return Mage_Eav_Model_Entity_Attribute | |
+ */ | |
+ protected function _getSearchableAttribute($attribute) | |
+ { | |
+ $attributes = $this->_getSearchableAttributes(); | |
+ if (is_numeric($attribute)) { | |
+ if (isset($attributes[$attribute])) { | |
+ return $attributes[$attribute]; | |
+ } | |
+ } elseif (is_string($attribute)) { | |
+ foreach ($attributes as $attributeModel) { | |
+ if ($attributeModel->getAttributeCode() == $attribute) { | |
+ return $attributeModel; | |
+ } | |
+ } | |
+ } | |
+ | |
+ return $this->getEavConfig()->getAttribute(Mage_Catalog_Model_Product::ENTITY, $attribute); | |
+ } | |
+ | |
+ /** | |
+ * Returns expresion for field unification | |
+ * | |
+ * @param string $field | |
+ * @param string $backendType | |
+ * @return Zend_Db_Expr | |
+ */ | |
+ protected function _unifyField($field, $backendType = 'varchar') | |
+ { | |
+ if ($backendType == 'datetime') { | |
+ $expr = Mage::getResourceHelper('catalogsearch')->castField( | |
+ $this->_getReadAdapter()->getDateFormatSql($field, '%Y-%m-%d %H:%i:%s')); | |
+ } else { | |
+ $expr = Mage::getResourceHelper('catalogsearch')->castField($field); | |
+ } | |
+ return $expr; | |
+ } | |
+ | |
+ /** | |
+ * Load product(s) attributes | |
+ * | |
+ * @param int $storeId | |
+ * @param array $productIds | |
+ * @param array $attributeTypes | |
+ * @return array | |
+ */ | |
+ protected function _getProductAttributes($storeId, array $productIds, array $attributeTypes) | |
+ { | |
+ $result = array(); | |
+ $selects = array(); | |
+ $websiteId = Mage::app()->getStore($storeId)->getWebsiteId(); | |
+ $adapter = $this->_getWriteAdapter(); | |
+ $ifStoreValue = $adapter->getCheckSql('t_store.value_id > 0', 't_store.value', 't_default.value'); | |
+ foreach ($attributeTypes as $backendType => $attributeIds) { | |
+ if ($attributeIds) { | |
+ $tableName = $this->getTable(array('catalog/product', $backendType)); | |
+ $select = $adapter->select() | |
+ ->from( | |
+ array('t_default' => $tableName), | |
+ array('entity_id', 'attribute_id')) | |
+ ->joinLeft( | |
+ array('t_store' => $tableName), | |
+ $adapter->quoteInto( | |
+ 't_default.entity_id=t_store.entity_id' . | |
+ ' AND t_default.attribute_id=t_store.attribute_id' . | |
+ ' AND t_store.store_id=?', | |
+ $storeId), | |
+ array('value' => $this->_unifyField($ifStoreValue, $backendType))) | |
+ ->where('t_default.store_id=?', 0) | |
+ ->where('t_default.attribute_id IN (?)', $attributeIds) | |
+ ->where('t_default.entity_id IN (?)', $productIds); | |
+ | |
+ /** | |
+ * Add additional external limitation | |
+ */ | |
+ Mage::dispatchEvent('prepare_catalog_product_index_select', array( | |
+ 'select' => $select, | |
+ 'entity_field' => new Zend_Db_Expr('t_default.entity_id'), | |
+ 'website_field' => $websiteId, | |
+ 'store_field' => new Zend_Db_Expr('t_store.store_id') | |
+ )); | |
+ | |
+ $selects[] = $select; | |
+ } | |
+ } | |
+ | |
+ if ($selects) { | |
+ $select = $adapter->select()->union($selects, Zend_Db_Select::SQL_UNION_ALL); | |
+ $query = $adapter->query($select); | |
+ while ($row = $query->fetch()) { | |
+ $result[$row['entity_id']][$row['attribute_id']] = $row['value']; | |
+ } | |
+ } | |
+ | |
+ return $result; | |
+ } | |
+ | |
+ /** | |
+ * Retrieve Product Type Instance | |
+ * | |
+ * @param string $typeId | |
+ * @return Mage_Catalog_Model_Product_Type_Abstract | |
+ */ | |
+ protected function _getProductTypeInstance($typeId) | |
+ { | |
+ if (!isset($this->_productTypes[$typeId])) { | |
+ $productEmulator = $this->_getProductEmulator(); | |
+ $productEmulator->setTypeId($typeId); | |
+ | |
+ $this->_productTypes[$typeId] = Mage::getSingleton('catalog/product_type') | |
+ ->factory($productEmulator); | |
+ } | |
+ return $this->_productTypes[$typeId]; | |
+ } | |
+ | |
+ /** | |
+ * Return all product children ids | |
+ * | |
+ * @param $productId | |
+ * @param $typeId | |
+ * @param null|int $websiteId | |
+ * @return array|null | |
+ */ | |
+ protected function _getProductChildrenIds($productId, $typeId, $websiteId = null) | |
+ { | |
+ $typeInstance = $this->_getProductTypeInstance($typeId); | |
+ $relation = $typeInstance->isComposite() | |
+ ? $typeInstance->getRelationInfo() | |
+ : false; | |
+ | |
+ if ($relation && $relation->getTable() && $relation->getParentFieldName() && $relation->getChildFieldName()) { | |
+ $select = $this->_getReadAdapter()->select() | |
+ ->from( | |
+ array('main' => $this->getTable($relation->getTable())), | |
+ array($relation->getChildFieldName())) | |
+ ->where("main.{$relation->getParentFieldName()} = ?", $productId); | |
+ if (!is_null($relation->getWhere())) { | |
+ $select->where($relation->getWhere()); | |
+ } | |
+ | |
+ Mage::dispatchEvent('prepare_product_children_id_list_select', array( | |
+ 'select' => $select, | |
+ 'entity_field' => 'main.product_id', | |
+ 'website_field' => $websiteId | |
+ )); | |
+ | |
+ return $this->_getReadAdapter()->fetchCol($select); | |
+ } | |
+ | |
+ return null; | |
+ } | |
+ | |
+ /** | |
+ * Return all product children ids | |
+ * | |
+ * @param int $productId Product Entity Id | |
+ * @param string $typeId Super Product Link Type | |
+ * @return array|null | |
+ */ | |
+ protected function _getProductChildIds($productId, $typeId) | |
+ { | |
+ return $this->_getProductChildrenIds($productId, $typeId); | |
+ } | |
+ | |
+ /** | |
+ * Retrieve Product Emulator (Varien Object) | |
+ * | |
+ * @return Varien_Object | |
+ */ | |
+ protected function _getProductEmulator() | |
+ { | |
+ $productEmulator = new Varien_Object(); | |
+ $productEmulator->setIdFieldName('entity_id'); | |
+ | |
+ return $productEmulator; | |
+ } | |
+ | |
+ /** | |
+ * Prepare Fulltext index value for product | |
+ * | |
+ * @param array $indexData | |
+ * @param array $productData | |
+ * @param int $storeId | |
+ * @return string | |
+ */ | |
+ protected function _prepareProductIndex($indexData, $productData, $storeId) | |
+ { | |
+ $index = array(); | |
+ | |
+ foreach ($this->_getSearchableAttributes('static') as $attribute) { | |
+ $attributeCode = $attribute->getAttributeCode(); | |
+ | |
+ if (isset($productData[$attributeCode])) { | |
+ $value = $this->_getAttributeValue($attribute->getId(), $productData[$attributeCode], $storeId); | |
+ if ($value) { | |
+ //For grouped products | |
+ if (isset($index[$attributeCode])) { | |
+ if (!is_array($index[$attributeCode])) { | |
+ $index[$attributeCode] = array($index[$attributeCode]); | |
+ } | |
+ $index[$attributeCode][] = $value; | |
+ } | |
+ //For other types of products | |
+ else { | |
+ $index[$attributeCode] = $value; | |
+ } | |
+ } | |
+ } | |
+ } | |
+ | |
+ foreach ($indexData as $entityId => $attributeData) { | |
+ foreach ($attributeData as $attributeId => $attributeValue) { | |
+ $value = $this->_getAttributeValue($attributeId, $attributeValue, $storeId); | |
+ if (!is_null($value) && $value !== false) { | |
+ $attributeCode = $this->_getSearchableAttribute($attributeId)->getAttributeCode(); | |
+ | |
+ if (isset($index[$attributeCode])) { | |
+ $index[$attributeCode][$entityId] = $value; | |
+ } else { | |
+ $index[$attributeCode] = array($entityId => $value); | |
+ } | |
+ } | |
+ } | |
+ } | |
+ | |
+ if (!$this->_engine->allowAdvancedIndex()) { | |
+ $product = $this->_getProductEmulator() | |
+ ->setId($productData['entity_id']) | |
+ ->setTypeId($productData['type_id']) | |
+ ->setStoreId($storeId); | |
+ $typeInstance = $this->_getProductTypeInstance($productData['type_id']); | |
+ if ($data = $typeInstance->getSearchableData($product)) { | |
+ $index['options'] = $data; | |
+ } | |
+ } | |
+ | |
+ if (isset($productData['in_stock'])) { | |
+ $index['in_stock'] = $productData['in_stock']; | |
+ } | |
+ | |
+ if ($this->_engine) { | |
+ return $this->_engine->prepareEntityIndex($index, $this->_separator); | |
+ } | |
+ | |
+ return Mage::helper('catalogsearch')->prepareIndexdata($index, $this->_separator); | |
+ } | |
+ | |
+ /** | |
+ * Retrieve attribute source value for search | |
+ * | |
+ * @param int $attributeId | |
+ * @param mixed $value | |
+ * @param int $storeId | |
+ * @return mixed | |
+ */ | |
+ protected function _getAttributeValue($attributeId, $value, $storeId) | |
+ { | |
+ $attribute = $this->_getSearchableAttribute($attributeId); | |
+ if (!$attribute->getIsSearchable()) { | |
+ if ($this->_engine->allowAdvancedIndex()) { | |
+ if ($attribute->getAttributeCode() == 'visibility') { | |
+ return $value; | |
+ } elseif (!($attribute->getIsVisibleInAdvancedSearch() | |
+ || $attribute->getIsFilterable() | |
+ || $attribute->getIsFilterableInSearch() | |
+ || $attribute->getUsedForSortBy()) | |
+ ) { | |
+ return null; | |
+ } | |
+ } else { | |
+ return null; | |
+ } | |
+ } | |
+ | |
+ if ($attribute->usesSource()) { | |
+ if ($this->_engine->allowAdvancedIndex()) { | |
+ return $value; | |
+ } | |
+ | |
+ $attribute->setStoreId($storeId); | |
+ $value = $attribute->getSource()->getIndexOptionText($value); | |
+ | |
+ if (is_array($value)) { | |
+ $value = implode($this->_separator, $value); | |
+ } elseif (empty($value)) { | |
+ $inputType = $attribute->getFrontend()->getInputType(); | |
+ if ($inputType == 'select' || $inputType == 'multiselect') { | |
+ return null; | |
+ } | |
+ } | |
+ } elseif ($attribute->getBackendType() == 'datetime') { | |
+ $value = $this->_getStoreDate($storeId, $value); | |
+ } else { | |
+ $inputType = $attribute->getFrontend()->getInputType(); | |
+ if ($inputType == 'price') { | |
+ $value = Mage::app()->getStore($storeId)->roundPrice($value); | |
+ } | |
+ } | |
+ | |
+ $value = preg_replace("#\s+#siu", ' ', trim(strip_tags($value))); | |
+ | |
+ return $value; | |
+ } | |
+ | |
+ /** | |
+ * Save Product index | |
+ * | |
+ * @param int $productId | |
+ * @param int $storeId | |
+ * @param string $index | |
+ * @return Mage_CatalogSearch_Model_Resource_Fulltext | |
+ */ | |
+ protected function _saveProductIndex($productId, $storeId, $index) | |
+ { | |
+ if ($this->_engine) { | |
+ $this->_engine->saveEntityIndex($productId, $storeId, $index); | |
+ } | |
+ | |
+ return $this; | |
+ } | |
+ | |
+ /** | |
+ * Save Multiply Product indexes | |
+ * | |
+ * @param int $storeId | |
+ * @param array $productIndexes | |
+ * @return Mage_CatalogSearch_Model_Resource_Fulltext | |
+ */ | |
+ protected function _saveProductIndexes($storeId, $productIndexes) | |
+ { | |
+ if ($this->_engine) { | |
+ $this->_engine->saveEntityIndexes($storeId, $productIndexes); | |
+ } | |
+ | |
+ return $this; | |
+ } | |
+ | |
+ /** | |
+ * Retrieve Date value for store | |
+ * | |
+ * @param int $storeId | |
+ * @param string $date | |
+ * @return string | |
+ */ | |
+ protected function _getStoreDate($storeId, $date = null) | |
+ { | |
+ if (!isset($this->_dates[$storeId])) { | |
+ $timezone = Mage::getStoreConfig(Mage_Core_Model_Locale::XML_PATH_DEFAULT_TIMEZONE, $storeId); | |
+ $locale = Mage::getStoreConfig(Mage_Core_Model_Locale::XML_PATH_DEFAULT_LOCALE, $storeId); | |
+ $locale = new Zend_Locale($locale); | |
+ | |
+ $dateObj = new Zend_Date(null, null, $locale); | |
+ $dateObj->setTimezone($timezone); | |
+ $this->_dates[$storeId] = array($dateObj, $locale->getTranslation(null, 'date', $locale)); | |
+ } | |
+ | |
+ if (!is_empty_date($date)) { | |
+ list($dateObj, $format) = $this->_dates[$storeId]; | |
+ $dateObj->setDate($date, Varien_Date::DATETIME_INTERNAL_FORMAT); | |
+ | |
+ return $dateObj->toString($format); | |
+ } | |
+ | |
+ return null; | |
+ } | |
+ | |
+ | |
+ | |
+ | |
+ | |
+ // Deprecated methods | |
+ | |
+ /** | |
+ * Set whether table changes are allowed | |
+ * | |
+ * @deprecated after 1.6.1.0 | |
+ * @param bool $value | |
+ * @return Mage_CatalogSearch_Model_Resource_Fulltext | |
+ */ | |
+ public function setAllowTableChanges($value = true) | |
+ { | |
+ $this->_allowTableChanges = $value; | |
+ return $this; | |
+ } | |
+ | |
+ /** | |
+ * Update category products indexes | |
+ * | |
+ * deprecated after 1.6.2.0 | |
+ * | |
+ * @param array $productIds | |
+ * @param array $categoryIds | |
+ * @return Mage_CatalogSearch_Model_Resource_Fulltext | |
+ */ | |
+ public function updateCategoryIndex($productIds, $categoryIds) | |
+ { | |
+ return $this; | |
+ } | |
+} | |
diff --git a/app/code/local/Mage/CatalogSearch/Model/Resource/Fulltext/Collection.php b/app/code/local/Mage/CatalogSearch/Model/Resource/Fulltext/Collection.php | |
new file mode 100644 | |
index 0000000..ff3686a | |
--- /dev/null | |
+++ app/code/local/Mage/CatalogSearch/Model/Resource/Fulltext/Collection.php | |
@@ -0,0 +1,258 @@ | |
+<?php | |
+/** | |
+ * Magento | |
+ * | |
+ * NOTICE OF LICENSE | |
+ * | |
+ * This source file is subject to the Open Software License (OSL 3.0) | |
+ * that is bundled with this package in the file LICENSE.txt. | |
+ * It is also available through the world-wide-web at this URL: | |
+ * http://opensource.org/licenses/osl-3.0.php | |
+ * If you did not receive a copy of the license and are unable to | |
+ * obtain it through the world-wide-web, please send an email | |
+ * to [email protected] so we can send you a copy immediately. | |
+ * | |
+ * DISCLAIMER | |
+ * | |
+ * Do not edit or add to this file if you wish to upgrade Magento to newer | |
+ * versions in the future. If you wish to customize Magento for your | |
+ * needs please refer to http://www.magento.com for more information. | |
+ * | |
+ * @category Mage | |
+ * @package Mage_CatalogSearch | |
+ * @copyright Copyright (c) 2006-2016 X.commerce, Inc. and affiliates (http://www.magento.com) | |
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) | |
+ */ | |
+ | |
+ | |
+/** | |
+ * Fulltext Collection | |
+ * | |
+ * @category Mage | |
+ * @package Mage_CatalogSearch | |
+ * @author Magento Core Team <[email protected]> | |
+ */ | |
+class Mage_CatalogSearch_Model_Resource_Fulltext_Collection extends Mage_Catalog_Model_Resource_Product_Collection | |
+{ | |
+ /** | |
+ * Name for relevance order | |
+ */ | |
+ const RELEVANCE_ORDER_NAME = 'relevance'; | |
+ | |
+ /** | |
+ * Found data | |
+ * | |
+ * @var array | |
+ */ | |
+ protected $_foundData = null; | |
+ | |
+ /** | |
+ * Sort order by relevance | |
+ * | |
+ * @var null | |
+ */ | |
+ protected $_relevanceSortOrder = SORT_DESC; | |
+ | |
+ /** | |
+ * Sort by relevance flag | |
+ * | |
+ * @var bool | |
+ */ | |
+ protected $_sortByRelevance = false; | |
+ | |
+ /** | |
+ * Is search filter applied flag | |
+ * | |
+ * @var bool | |
+ */ | |
+ protected $_isSearchFiltersApplied = false; | |
+ | |
+ /** | |
+ * Retrieve query model object | |
+ * | |
+ * @return Mage_CatalogSearch_Model_Query | |
+ */ | |
+ protected function _getQuery() | |
+ { | |
+ return Mage::helper('catalogsearch')->getQuery(); | |
+ } | |
+ | |
+ /** | |
+ * Add search query filter | |
+ * | |
+ * @param $query | |
+ * @return Mage_CatalogSearch_Model_Resource_Fulltext_Collection | |
+ */ | |
+ public function addSearchFilter($query) | |
+ { | |
+ return $this; | |
+ } | |
+ | |
+ /** | |
+ * Before load handler | |
+ * | |
+ * @return Mage_Catalog_Model_Resource_Product_Collection | |
+ */ | |
+ protected function _beforeLoad() | |
+ { | |
+ if (!$this->_isSearchFiltersApplied) { | |
+ $this->_applySearchFilters(); | |
+ } | |
+ | |
+ return parent::_beforeLoad(); | |
+ } | |
+ | |
+ /** | |
+ * Get collection size | |
+ * | |
+ * @return int | |
+ */ | |
+ public function getSize() | |
+ { | |
+ if (!$this->_isSearchFiltersApplied) { | |
+ $this->_applySearchFilters(); | |
+ } | |
+ | |
+ return parent::getSize(); | |
+ } | |
+ | |
+ /** | |
+ * Apply collection search filter | |
+ * | |
+ * @return Mage_CatalogSearch_Model_Resource_Fulltext_Collection | |
+ */ | |
+ protected function _applySearchFilters() | |
+ { | |
+ $foundIds = $this->getFoundIds(); | |
+ if (!empty($foundIds)) { | |
+ $this->addIdFilter($foundIds); | |
+ } else { | |
+ /** | |
+ * @see http://magento.stackexchange.com/questions/140707/magento-1-9-3-every-product-is-displayed-in-the-search-in-full-text-mode | |
+ */ | |
+ $this->getSelect()->where('FALSE'); // replacement | |
+ } | |
+ $this->_isSearchFiltersApplied = true; | |
+ | |
+ return $this; | |
+ } | |
+ | |
+ /** | |
+ * Get found products ids | |
+ * | |
+ * @return array | |
+ */ | |
+ public function getFoundIds() | |
+ { | |
+ if (is_null($this->_foundData)) { | |
+ /** @var Mage_CatalogSearch_Model_Fulltext $preparedResult */ | |
+ $preparedResult = Mage::getSingleton('catalogsearch/fulltext'); | |
+ $preparedResult->prepareResult(); | |
+ $this->_foundData = $preparedResult->getResource()->getFoundData(); | |
+ } | |
+ if (isset($this->_orders[self::RELEVANCE_ORDER_NAME])) { | |
+ $this->_resortFoundDataByRelevance(); | |
+ } | |
+ return array_keys($this->_foundData); | |
+ } | |
+ | |
+ /** | |
+ * Resort found data by relevance | |
+ * | |
+ * @return Mage_CatalogSearch_Model_Resource_Fulltext_Collection | |
+ */ | |
+ protected function _resortFoundDataByRelevance() | |
+ { | |
+ if (is_array($this->_foundData)) { | |
+ $data = array(); | |
+ foreach ($this->_foundData as $id => $relevance) { | |
+ $this->_foundData[$id] = $relevance . '_' . $id; | |
+ } | |
+ natsort($this->_foundData); | |
+ if ($this->_relevanceSortOrder == SORT_DESC) { | |
+ $this->_foundData = array_reverse($this->_foundData); | |
+ } | |
+ foreach ($this->_foundData as $dataString) { | |
+ list ($relevance, $id) = explode('_', $dataString); | |
+ $data[$id] = $relevance; | |
+ } | |
+ $this->_foundData = $data; | |
+ } | |
+ return $this; | |
+ } | |
+ | |
+ /** | |
+ * Set Order field | |
+ * | |
+ * @param string $attribute | |
+ * @param string $dir | |
+ * @return Mage_CatalogSearch_Model_Resource_Fulltext_Collection | |
+ */ | |
+ public function setOrder($attribute, $dir = 'desc') | |
+ { | |
+ if ($attribute == 'relevance') { | |
+ $this->_relevanceSortOrder = ($dir == 'asc') ? SORT_ASC : SORT_DESC; | |
+ $this->addOrder(self::RELEVANCE_ORDER_NAME); | |
+ } else { | |
+ parent::setOrder($attribute, $dir); | |
+ } | |
+ return $this; | |
+ } | |
+ | |
+ /** | |
+ * Add sorting by relevance to select | |
+ * | |
+ * @return Mage_CatalogSearch_Model_Resource_Fulltext_Collection | |
+ */ | |
+ protected function _addRelevanceSorting() | |
+ { | |
+ $foundIds = $this->getFoundIds(); | |
+ if (!$foundIds) { | |
+ return $this; | |
+ } | |
+ | |
+ /** @var Mage_CatalogSearch_Model_Resource_Helper_Mysql4 $resourceHelper */ | |
+ $resourceHelper = Mage::getResourceHelper('catalogsearch'); | |
+ $this->_select->order( | |
+ new Zend_Db_Expr( | |
+ $resourceHelper->getFieldOrderExpression( | |
+ 'e.' . $this->getResource()->getIdFieldName(), | |
+ $foundIds | |
+ ) | |
+ . ' ' . Zend_Db_Select::SQL_ASC | |
+ ) | |
+ ); | |
+ | |
+ return $this; | |
+ } | |
+ | |
+ /** | |
+ * Stub method for compatibility with other search engines | |
+ * | |
+ * @return Mage_CatalogSearch_Model_Resource_Fulltext_Collection | |
+ */ | |
+ public function setGeneralDefaultQuery() | |
+ { | |
+ return $this; | |
+ } | |
+ | |
+ /** | |
+ * Render sql select orders | |
+ * | |
+ * @return Varien_Data_Collection_Db | |
+ */ | |
+ protected function _renderOrders() | |
+ { | |
+ if (!$this->_isOrdersRendered) { | |
+ foreach ($this->_orders as $attribute => $direction) { | |
+ if ($attribute == self::RELEVANCE_ORDER_NAME) { | |
+ $this->_addRelevanceSorting(); | |
+ } else { | |
+ $this->addAttributeToSort($attribute, $direction); | |
+ } | |
+ } | |
+ $this->_isOrdersRendered = true; | |
+ } | |
+ return $this; | |
+ } | |
+} | |
-- | |
2.8.1 | |