Last Monday we have described how to enable PrestaShop CMS search. Today we will explore how to make this search more flexible and how to create module settings (remember, that we have already posted the module to enable PrestaShop CMS search).
Let’s start with updating the module to 2.0 version. To do that, change the $this->version = ‘2.0.0’ parameter in the module constructor. Now we need to write a module with function, similar to standard search in PrestaShop. Create two tables, belvg_cms_search_index and belvg_cms_search_word, to be used for articles indexation. Don’t forget to add the indexed flag, noting that the article is indexed.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
$sql[] = 'CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.'belvg_cms_search_index` ( `id_cms` int(11) unsigned NOT NULL, `id_word` int(11) unsigned NOT NULL, `weight` smallint(4) unsigned NOT NULL default 1, PRIMARY KEY (`id_word`, `id_cms`) ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8'; $sql[] = 'CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.'belvg_cms_search_word` ( `id_word` int(10) unsigned NOT NULL auto_increment, `id_lang` int(10) unsigned NOT NULL, `word` varchar(15) NOT NULL, PRIMARY KEY (`id_word`), UNIQUE KEY `id_lang` (`id_lang`,`word`) ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8'; $sql = 'SELECT * FROM `'._DB_PREFIX_.'cms`'; $check_array = Db::getInstance()->getRow($sql); if (!array_key_exists('indexed', $check_array)){ if (!Db::getInstance()->Execute('ALTER TABLE `'._DB_PREFIX_.'cms` ADD `indexed` TINYINT( 1 ) NOT NULL DEFAULT \'0\'')) return false; } |
PrestaShop Support & Maintenance
Take your online store to the next level with BelVG PrestaShop Support & Maintenance
Visit the pageIn order to index an article while saving and updating articles, we change CMS class by extending the functionality of add () and update () methods:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public function add($autodate = true, $nullValues = false) { $success = parent::add($autodate, $nullValues); if ($success) CMS::indexation(false, $this->id); return $success; } public function update($nullValues = false) { $success = parent::update($autodate, $nullValues); if ($success) CMS::indexation(false, $this->id); return $success; } |
indexation () method enters data about articles in index tables.
1 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 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
public static function indexation($full = false, $id_cms = false) { $db = Db::getInstance(); if ($id_cms) $full = false; if ($full) { $db->Execute('TRUNCATE '._DB_PREFIX_.'belvg_cms_search_index'); $db->Execute('TRUNCATE '._DB_PREFIX_.'belvg_cms_search_word'); $db->Execute('UPDATE '._DB_PREFIX_.'cms SET indexed = 0'); } else { // Do it even if you already know the product id in order to be sure that it exists $cms_articles = $db->ExecuteS(' SELECT id_cms FROM '._DB_PREFIX_.'cms WHERE id_cms = '.(int)$id_cms); $ids = array(); if ($cms_articles) foreach($cms_articles AS $article) $ids[] = (int)$article['id_cms']; if (sizeof($ids)){ $db->Execute('DELETE FROM '._DB_PREFIX_.'belvg_cms_search_index WHERE id_cms IN ('.implode(',', $ids).')'); $db->Execute('UPDATE '._DB_PREFIX_.'cms SET indexed = 0 WHERE id_cms IN ('.implode(',', $ids).')'); } } // Every fields are weighted according to the configuration in the backend $weightArray = array( 'meta_title' => Configuration::get('BSF_WEIGHT_TITLE'), 'meta_description' => Configuration::get('BSF_WEIGHT_DESC'), 'meta_keywords' => Configuration::get('BSF_WEIGHT_KEYWORDS'), 'content' => Configuration::get('BSF_WEIGHT_CONTENT'), ); // Those are kind of global variables required to save the processed data in the database every X occurences, in order to avoid overloading MySQL $countWords = 0; $countArticles = 0; $queryArray3 = array(); $articlesArray = array(); // Every indexed words are cached into a PHP array $wordIdsByWord = array(); $wordIds = Db::getInstance()->ExecuteS(' SELECT sw.id_word, sw.word, id_lang FROM '._DB_PREFIX_.'belvg_cms_search_word sw', false); $wordIdsByWord = array(); while ($wordId = $db->nextRow($wordIds)) { if (!isset($wordIdsByWord[$wordId['id_lang']])) $wordIdsByWord[$wordId['id_lang']] = array(); $wordIdsByWord[$wordId['id_lang']]['_'.$wordId['word']] = (int)$wordId['id_word']; } // Retrieve the number of languages $total_languages = count(Language::getLanguages(false)); // Articles are processed 50 by 50 in order to avoid overloading MySQL while (($cms_articles = CMS::getArticlesToIndex($total_languages, $id_cms, 50)) && is_array($cms_articles) && (count($cms_articles) > 0)) { // Now each non-indexed product is processed one by one, langage by langage foreach ($cms_articles as $article) { // Data must be cleaned of html, bad characters, spaces and anything, then if the resulting words are long enough, they're added to the array $pArray = array(); foreach ($article AS $key => $value) if (strncmp($key, 'id_', 3)) { $words = explode(' ', Search::sanitize($value, (int)$article['id_lang'], true)); foreach ($words AS $word) if (!empty($word)) { $word = Tools::substr($word, 0, PS_SEARCH_MAX_WORD_LENGTH); // Remove accents $word = Tools::replaceAccentedChars($word); if (!isset($pArray[$word])) $pArray[$word] = 0; $pArray[$word] += $weightArray[$key]; } } // If we find words that need to be indexed, they're added to the word table in the database if (sizeof($pArray)) { $queryArray = array(); $queryArray2 = array(); foreach ($pArray AS $word => $weight) if ($weight AND !isset($wordIdsByWord['_'.$word])) { $queryArray[$word] = '('.(int)$article['id_lang'].',\''.pSQL($word).'\')'; $queryArray2[] = '\''.pSQL($word).'\''; $wordIdsByWord[$article['id_lang']]['_'.$word] = 0; } $existingWords = $db->ExecuteS(' SELECT DISTINCT word FROM '._DB_PREFIX_.'belvg_cms_search_word WHERE word IN ('.implode(',', $queryArray2).') AND id_lang = '.(int)$article['id_lang']); foreach ($existingWords as $data) unset($queryArray[Tools::replaceAccentedChars($data['word'])]); if (count($queryArray)) { // The words are inserted... $db->Execute(' INSERT IGNORE INTO '._DB_PREFIX_.'belvg_cms_search_word (id_lang, word) VALUES '.implode(',', $queryArray)); } if (count($queryArray2)) { // ...then their IDs are retrieved and added to the cache $addedWords = $db->ExecuteS(' SELECT sw.id_word, sw.word FROM '._DB_PREFIX_.'belvg_cms_search_word sw WHERE sw.word IN ('.implode(',', $queryArray2).') AND sw.id_lang = '.(int)$article['id_lang'].' LIMIT '.count($queryArray2)); // replace accents from the retrieved words so that words without accents or with differents accents can still be linked foreach ($addedWords AS $wordId) $wordIdsByWord[$article['id_lang']]['_'.Tools::replaceAccentedChars($wordId['word'])] = (int)$wordId['id_word']; } } foreach ($pArray AS $word => $weight) { if (!$weight) continue; if (!isset($wordIdsByWord[$article['id_lang']]['_'.$word])) continue; if (!$wordIdsByWord[$article['id_lang']]['_'.$word]) continue; $queryArray3[] = '('.(int)$article['id_cms'].','.(int)$wordIdsByWord[$article['id_lang']]['_'.$word].','.(int)$weight.')'; // Force save every 200 words in order to avoid overloading MySQL if (++$countWords % 200 == 0) CMS::saveIndex($queryArray3); } if (!in_array($article['id_cms'], $articlesArray)) $articlesArray[] = (int)$article['id_cms']; } CMS::setArticlesAsIndexed($articlesArray); // One last save is done at the end in order to save what's left CMS::saveIndex($queryArray3); } return true; } |
getArticlesToIndex () method returns a list of articles, needed to be indexed, or if the article identifier was transferred, it checks whether the article requires indexation. saveIndex () method is responsible for recording new indexes data in the database. SetArticlesAsIndexed () marks the indexed flag we created earlier in cms table.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
protected static function getArticlesToIndex($total_languages, $id_cms = false, $limit = 50) { $limit = max(1, round($limit / $total_languages) * $total_languages); return Db::getInstance()->ExecuteS(' SELECT c.`id_cms`, cl.`meta_title`, cl.`meta_description`, cl.`meta_keywords`, cl.`content`, cl.`id_lang` FROM `'._DB_PREFIX_.'cms` c JOIN `'._DB_PREFIX_.'cms_lang` cl ON (c.`id_cms` = cl.`id_cms`) WHERE c.indexed = 0 '.($id_cms ? 'AND c.id_cms = '.(int)$id_cms : '').' LIMIT '.(int)$limit); } protected static function saveIndex(&$queryArray3) { if (count($queryArray3)) Db::getInstance()->Execute('INSERT INTO '._DB_PREFIX_.'belvg_cms_search_index (id_cms, id_word, weight) VALUES '.implode(',', $queryArray3).' ON DUPLICATE KEY UPDATE weight = weight + VALUES(weight)'); $queryArray3 = array(); } protected static function setArticlesAsIndexed(&$articlesArray) { if ($i = count($articlesArray)) Db::getInstance()->Execute('UPDATE '._DB_PREFIX_.'cms SET indexed = 1 WHERE id_cms IN ('.implode(',', $articlesArray).') LIMIT '.(int)$i); $articlesArray = array(); } |
The most important is find () method, which selects articles by comparing the search query with words in the index table.
1 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 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
public static function find($id_lang, $expr) { global $cookie; $db = Db::getInstance(_PS_USE_SQL_SLAVE_); $words = explode(' ', Search::sanitize($expr, (int)$id_lang)); $intersectArray = array(); foreach ($words AS $key => $word) if (!empty($word) AND strlen($word) >= (int)Configuration::get('PS_SEARCH_MINWORDLEN')) { $word = str_replace('%', '\\%', $word); $word = str_replace('_', '\\_', $word); $intersectArray[] = 'SELECT id_cms FROM '._DB_PREFIX_.'belvg_cms_search_word sw LEFT JOIN '._DB_PREFIX_.'belvg_cms_search_index si ON sw.id_word = si.id_word WHERE sw.id_lang = '.(int)$id_lang.' AND sw.word LIKE '.($word[0] == '-' ? ' \''.pSQL(Tools::substr($word, 1, PS_SEARCH_MAX_WORD_LENGTH)).'%\'' : '\''.pSQL(Tools::substr($word, 0, PS_SEARCH_MAX_WORD_LENGTH)).'%\'' ); if ($word[0] != '-') $scoreArray[] = 'sw.word LIKE \''.pSQL(Tools::substr($word, 0, PS_SEARCH_MAX_WORD_LENGTH)).'%\''; } else unset($words[$key]); $eligibleArticles = array(); foreach ($intersectArray as $query) { $result = $db->ExecuteS($query, false); $eligibleArticles = array(); while ($row = $db->nextRow($result)) $eligibleArticles[] = $row['id_cms']; if (!count($eligibleArticles)) return array(); } array_unique($eligibleArticles); $productPool = ''; foreach ($eligibleArticles AS $id_cms) if ($id_cms) $productPool .= (int)$id_cms.','; $productPool = ((strpos($productPool, ',') === false) ? (' = '.(int)$productPool.' ') : (' IN ('.rtrim($productPool, ',').') ')); $sql = 'SELECT * FROM `'._DB_PREFIX_.'cms_lang` WHERE id_lang = '.$id_lang.' AND `id_cms` '.$productPool; $articles = Db::getInstance()->ExecuteS($sql); foreach($articles as &$article){ $article['content'] = strip_tags($article['content']); } return $articles; } |
It’s time to create module settings.
Partner With Us
Let's discuss how to grow your business. Get a Free Quote.To allow users to customize the module, you need to implement a getContent () method in the belvg_cmssearchfree.php file, which should return the html, containing a form with settings.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public function getContent(){ global $smarty, $cookie; if(Tools::isSubmit('submitUpdate')) { Configuration::updateValue('BSF_USE_INDEX', (int)Tools::getValue('use_index')); Configuration::updateValue('BSF_WEIGHT_TITLE', (int)Tools::getValue('meta_title')); Configuration::updateValue('BSF_WEIGHT_DESC', (int)Tools::getValue('meta_description')); Configuration::updateValue('BSF_WEIGHT_KEYWORDS', (int)Tools::getValue('meta_keywords')); Configuration::updateValue('BSF_WEIGHT_CONTENT', (int)Tools::getValue('content')); $smarty->assign(array( 'save_ok' => true )); } if(Tools::isSubmit('submitReindexAll')) { if( CMS::indexation(true) ){ $smarty->assign(array( 'reindex_ok' => true )); } } $this->_html .= $this->_displayForm(); return $this->_html; } |
We use here smarty template modules/belvg_cmssearchfree/tpl/adm/content.tpl:
1 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 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
<form enctype="multipart/form-data" action="{$server}" method="post" id="slider-form"> <h2>{l s='Belvg Cms Search Free' mod='belvg_cmssearchfree'}</h2> <fieldset style="margin-top:10px"> <legend><div id="setting" class="icon"></div>{l s='Developed by BelVG' mod='belvg_cmssearchfree'}</legend> <div style="padding:10px;background-color:#fff;border:1px solid #ddd;margin-bottom:7px;"> {l s='This module was developed by' mod='belvg_cmssearchfree'} <a style="color:#268CCD;text-decoration:underline" target="_blank" href="http://www.belvg.com">www.BelVG.com</a>. {l s='For docs & examples refer to the BelVG' mod='belvg_cmssearchfree'} <a target="_blank" style="color:#268CCD;text-decoration:underline" href="http://module-presta.com">{l s='web site' mod='belvg_cmssearchfree'}</a>. </div> </fieldset> {if isset($save_ok) && $save_ok} <div class="conf"> <img src="../img/admin/ok2.png" alt=""> {l s='Settings was updated successfully' mod='belvg_cmssearchfree'} </div> {/if} {if isset($reindex_ok) && $reindex_ok} <div class="conf"> <img src="../img/admin/ok2.png" alt=""> {l s='New index was built successfully' mod='belvg_cmssearchfree'} </div> {/if} <fieldset style="margin-top:10px" class="inside-fieldset"> <legend><div id="setting" class="icon"></div>{l s='Settings' mod='belvg_cmssearchfree'}</legend> <label>{l s='Use index' mod='belvg_cmssearchfree'}:</label> <div class="margin-form"> <select name="use_index" style="width: 380px"> <option value="1" {if (isset($use_index) && $use_index=='1')} selected="selected" {/if}>{l s='Yes' mod='belvg_cmssearchfree'}</option> <option value="2" {if (isset($use_index) && $use_index=='2')} selected="selected" {/if}>{l s='No' mod='belvg_cmssearchfree'}</option> </select> </div> <div class="margin-form"> <input type="submit" value="{l s='Reindex All' mod='belvg_cmssearchfree'}" name="submitReindexAll" class="button"> </div> </fieldset> <fieldset style="margin-top:10px" class="inside-fieldset"> <legend><div id="setting" class="icon"></div>{l s='Weight' mod='belvg_cmssearchfree'}</legend> <div style="clear: both; padding-top:15px;"> <label>{l s='Meta title' mod='belvg_cmssearchfree'}</label> <div class="margin-form" style="padding-top:5px;"> <input type="text" size="4" name="meta_title" value="{if (isset($meta_title) && !empty($meta_title))} {$meta_title} {/if}"> </div> </div> <div style="clear: both; padding-top:15px;"> <label>{l s='Meta description' mod='belvg_cmssearchfree'}</label> <div class="margin-form" style="padding-top:5px;"> <input type="text" size="4" name="meta_description" value="{if (isset($meta_description) && !empty($meta_description))} {$meta_description} {/if}"> </div> </div> <div style="clear: both; padding-top:15px;"> <label>{l s='Meta keywords' mod='belvg_cmssearchfree'}</label> <div class="margin-form" style="padding-top:5px;"> <input type="text" size="4" name="meta_keywords" value="{if (isset($meta_keywords) && !empty($meta_keywords))} {$meta_keywords} {/if}"> </div> </div> <div style="clear: both; padding-top:15px;"> <label>{l s='Content' mod='belvg_cmssearchfree'}</label> <div class="margin-form" style="padding-top:5px;"> <input type="text" size="4" name="content" value="{if (isset($content) && !empty($content))} {$content} {/if}"> </div> </div> </fieldset> <p> <input class="button" type="submit" name="submitUpdate" value="{l s='Save configuration' mod='belvg_cmssearchfree'}" /> </p> </form> |
Download the full module version belvg_cmssearchfree. We are looking forward to your ideas and discussions in the comment form below.
Dedicated PrestaShop Developers
Take your online store to the next level with BelVG Dedicated PrestaShop Developers
Visit the page
Its very interesting your module, it is compatible with PS1.6???
We have updated the CMS Search module for Prestashop 1.5. Head to our store to get it – http://module-presta.com/cms-search.html.
But that’s not all – we will be offering the new version of the module for free to several blog readers who were interested in it’s appearance enough to ask us about it. If you wrote a comment to this post – please contact us at [email protected] and we will give you a special download code.
Hi, this project is still active? Or is it dead?
Is there already a solution for 1.5? Would be very nice.
Sorry for bad english. I’m German …
Elaine,
Yes, we remember about this module. We will try to remake it this month since there are already
several users who have been waiting for this.
Hi Alex,
Have you had any progress on updating the module for 1.5?
Thomas,
This module works only with Prestashop 1.4, but I am going to update it in the nearest future. So stay tuned!
I have Prestashop 1.5.2 and it’s not working
Pierre
Please send your FTP and admin accesses on [email protected] and we will fix it.
Hi,
very good and interesting developpement,
but i have the same problem like Pawel !
I use Prestashop 1.4.4.1
Could you help me _
Thanks very much
Pierre
I’ve sent it on email.
Thanks-
Pawel,
This problem has probably occurred because of incorrect module installation and/or configuration. Would you please send us on [email protected] or via Live Chat tab FTP and admin accesses so that our developers could check out and fix your issue?
On my it is not working.
I think that problem is that module is not indexing my pages.
When i am using reindex all button i am getting page just to path bar and then it is cut.
Where is the problem laying?
I am using 1.4.4.1