-
-
Save touol/a1c46d7fd199c0889c361c4075c28d39 to your computer and use it in GitHub Desktop.
<?php | |
/** | |
* The base class for mSearch2. | |
* | |
* @package msearch2 | |
*/ | |
class mSearch2 { | |
/** @var modX $modx */ | |
public $modx; | |
/** @var array $config */ | |
public $config = array(); | |
/** @var mSearch2ControllerRequest $request */ | |
protected $request; | |
/** @var mse2FiltersHandler $filtersHandler */ | |
public $filtersHandler = null; | |
/** @var array $phpMorphy */ | |
public $phpMorphy = array(); | |
/** @var array $methods */ | |
public $methods = array(); | |
/** @var array $filters */ | |
public $filters = array(); | |
/** @var array $aliases */ | |
public $aliases = array(); | |
/** @var integer Total number of filter operations */ | |
public $filter_operations = 0; | |
/** @var string Current search query */ | |
public $query = ''; | |
/** @var pdoFetch $pdoTools */ | |
public $pdoTools = null; | |
/** @var array $fields */ | |
public $fields = array(); | |
const version = '1.10.6'; | |
/** | |
* mSearch2 constructor. | |
* | |
* @param modX $modx | |
* @param array $config | |
*/ | |
public function __construct(modX &$modx, array $config = array()) { | |
$this->modx =& $modx; | |
$corePath = $this->modx->getOption('msearch2.core_path', $config, $this->modx->getOption('core_path') . 'components/msearch2/'); | |
$assetsPath = $this->modx->getOption('msearch2.assets_path', $config, $this->modx->getOption('assets_path') . 'components/msearch2/'); | |
$assetsUrl = $this->modx->getOption('msearch2.assets_url', $config, $this->modx->getOption('assets_url') . 'components/msearch2/'); | |
$actionUrl = $this->modx->getOption('msearch2.action_url', $config, $assetsUrl . 'action.php'); | |
$connectorUrl = $assetsUrl . 'connector.php'; | |
$this->config = array_merge(array( | |
'assetsUrl' => $assetsUrl, | |
'cssUrl' => $assetsUrl . 'css/', | |
'jsUrl' => $assetsUrl . 'js/', | |
'jsPath' => $assetsPath . 'js/', | |
'imagesUrl' => $assetsUrl . 'images/', | |
'customPath' => $corePath . 'custom/', | |
'dictsPath' => $corePath . 'phpmorphy/dicts/', | |
'connectorUrl' => $connectorUrl, | |
'actionUrl' => $actionUrl, | |
'corePath' => $corePath, | |
'modelPath' => $corePath . 'model/', | |
'templatesPath' => $corePath . 'elements/templates/', | |
'processorsPath' => $corePath . 'processors/', | |
'cacheTime' => 1800, | |
'min_word_length' => $this->modx->getOption('mse2_index_min_words_length', null, 3, true), | |
'exact_match_bonus' => $this->modx->getOption('mse2_search_exact_match_bonus', null, 10, true), | |
'like_match_bonus' => $this->modx->getOption('mse2_search_like_match_bonus', null, 3, true), | |
'all_words_bonus' => $this->modx->getOption('mse2_search_all_words_bonus', null, 5, true), | |
'old_search_algorithm' => $this->modx->getOption('mse2_old_search_algorithm', null, false), | |
'introCutBefore' => 50, | |
'introCutAfter' => 250, | |
'filter_delimeter' => '|', | |
'method_delimeter' => ':', | |
'values_delimeter' => ',', | |
'split_words' => $this->modx->getOption('mse2_search_split_words', null, '#\s#', true), | |
'split_all' => $this->modx->getOption('mse2_index_split_words', null, '#\s|[,.:;!?"\'(){}\\/\#]#', true), | |
'index_all' => $this->modx->getOption('mse2_index_all', null, false), | |
'suggestionsRadio' => array(), | |
'autocomplete' => 0, | |
'queryVar' => 'query', | |
'minQuery' => 3, | |
'onlyIndex' => false, | |
'config_file' => $assetsPath . 'js/web/config.js', | |
), $config); | |
if (!is_array($this->config['suggestionsRadio'])) { | |
$this->config['suggestionsRadio'] = array_map('trim', explode(',', $this->config['suggestionsRadio'])); | |
} | |
mb_internal_encoding($this->modx->getOption('modx_charset', null, 'UTF-8')); | |
$this->modx->addPackage('msearch2', $this->config['modelPath']); | |
$this->modx->lexicon->load('msearch2:default'); | |
$this->pdoTools = $this->modx->getService('pdoFetch'); | |
$fqn = $this->modx->getOption('pdoFetch.class', null, 'pdotools.pdofetch', true); | |
$path = $this->modx->getOption('pdofetch_class_path', null, MODX_CORE_PATH . 'components/pdotools/model/', true); | |
if ($pdoClass = $modx->loadClass($fqn, $path, false, true)) { | |
$this->pdoTools = new $pdoClass($modx, $config); | |
} else { | |
$this->pdoTools = $this->modx->getService('pdoFetch'); | |
} | |
$this->getWorkFields(); | |
//$this->checkStat(); | |
} | |
/** | |
* @param string $query | |
* | |
* @return string | |
*/ | |
public function getQuery($query = '') { | |
$query = htmlspecialchars_decode($query, ENT_QUOTES); | |
$query = strip_tags($query); | |
$query = preg_replace('#["\'\(\)\[\]\{\}]#', '', $query); | |
$query = htmlspecialchars($query); | |
return trim($query); | |
} | |
/** | |
* Prepares working fields of resources for search | |
* | |
* @param array $config | |
* | |
* @return array | |
*/ | |
public function getWorkFields($config = array()) { | |
$config = array_merge($this->config, $config); | |
$setting = $this->modx->getOption('mse2_index_fields', null, 'content:3,description:2,introtext:2,pagetitle:3,longtitle:3', true); | |
$fields = $default = array(); | |
// Preparing default fields for work | |
$tmp = array_map('trim', explode(',', strtolower($setting))); | |
foreach ($tmp as $v) { | |
$tmp2 = explode(':', $v); | |
$default[$tmp2[0]] = !empty($tmp2[1]) | |
? $tmp2[1] | |
: 1; | |
} | |
if ($this->modx->getOption('mse2_index_comments', null, true)) { | |
$default['comment'] = $this->modx->getOption('mse2_index_comments', null, 1, true); | |
} | |
if (!empty($config['fields'])) { | |
$tmp = array_map('trim', explode(',', strtolower($config['fields']))); | |
foreach ($tmp as $v) { | |
$tmp2 = explode(':', $v); | |
$fields[$tmp2[0]] = !empty($tmp2[1]) | |
? $tmp2[1] | |
: (isset($default[$tmp[0]]) | |
? $default[$tmp[0]] | |
: 1 | |
); | |
} | |
} | |
else { | |
$fields = $default; | |
} | |
$this->fields = $fields; | |
return $fields; | |
} | |
/** | |
* Initializes mSearch2 into different contexts. | |
* | |
* @param string $ctx The context to load. Defaults to web. | |
* @param array $scriptProperties | |
* | |
* @return boolean | |
*/ | |
public function initialize($ctx = 'web', $scriptProperties = array()) { | |
switch ($ctx) { | |
case 'mgr': | |
if (!$this->modx->loadClass('msearch2.request.mSearch2ControllerRequest', $this->config['modelPath'], true, true)) { | |
return 'Could not load controller request handler.'; | |
} | |
$this->request = new mSearch2ControllerRequest($this); | |
return $this->request->handleRequest(); | |
break; | |
default: | |
$this->config = array_merge($this->config, $scriptProperties); | |
$this->config['ctx'] = $ctx; | |
if (!defined('MODX_API_MODE') || !MODX_API_MODE) { | |
$config = $this->makePlaceholders($this->config); | |
if ($css = trim($this->modx->getOption('mse2_frontend_css'))) { | |
$this->modx->regClientCSS(str_replace($config['pl'], $config['vl'], $css)); | |
} | |
if ($js = trim($this->modx->getOption('mse2_frontend_js'))) { | |
$this->modx->regClientScript(str_replace($config['pl'], $config['vl'], $js)); | |
} | |
} | |
} | |
return true; | |
} | |
/** | |
* Method loads custom classes from specified directory | |
* | |
* @var string $dir Directory for load classes | |
* @return void | |
*/ | |
public function loadCustomClasses($dir) | |
{ | |
$customPath = $this->config['customPath']; | |
$placeholders = array( | |
'base_path' => MODX_BASE_PATH, | |
'core_path' => MODX_CORE_PATH, | |
'assets_path' => MODX_ASSETS_PATH, | |
); | |
$pl1 = $this->pdoTools->makePlaceholders($placeholders, '', '[[+', ']]', false); | |
$pl2 = $this->pdoTools->makePlaceholders($placeholders, '', '[[++', ']]', false); | |
$pl3 = $this->pdoTools->makePlaceholders($placeholders, '', '{', '}', false); | |
$customPath = str_replace($pl1['pl'], $pl1['vl'], $customPath); | |
$customPath = str_replace($pl2['pl'], $pl2['vl'], $customPath); | |
$customPath = str_replace($pl3['pl'], $pl3['vl'], $customPath); | |
if (strpos($customPath, MODX_BASE_PATH) === false && strpos($customPath, MODX_CORE_PATH) === false) { | |
$customPath = MODX_BASE_PATH . ltrim($customPath, '/'); | |
} | |
$customPath = rtrim($customPath, '/') . '/' . ltrim($dir, '/'); | |
if (file_exists($customPath) && $files = scandir($customPath)) { | |
foreach ($files as $file) { | |
if (preg_match('#\.class\.php$#i', $file)) { | |
/** @noinspection PhpIncludeInspection */ | |
include $customPath . '/' . $file; | |
} | |
} | |
} else { | |
$this->modx->log(modX::LOG_LEVEL_ERROR, "[mSearch2] Custom path is not exists: \"{$customPath}\""); | |
} | |
} | |
/** | |
* Initializes phpMorphy for needed language | |
* | |
* @return boolean | |
*/ | |
public function loadPhpMorphy() { | |
if (!class_exists('phpMorphy_Exception')) { | |
/** @noinspection PhpIncludeInspection */ | |
require_once $this->config['corePath'] . 'phpmorphy/src/common.php'; | |
} | |
$dicts = $this->getDictionaries(); | |
foreach (array_keys($dicts) as $lang) { | |
if (!empty($this->phpMorphy[$lang]) && $this->phpMorphy[$lang] instanceof phpMorphy) { | |
continue; | |
} | |
else { | |
try { | |
$this->phpMorphy[$lang] = new phpMorphy( | |
$this->config['corePath'] . 'phpmorphy/dicts/', | |
$lang, | |
array( | |
'storage' => 'file', | |
) | |
); | |
} | |
catch (phpMorphy_Exception $e) { | |
$this->modx->log(modX::LOG_LEVEL_ERROR, '[mSearch2] Could not initialize phpMorphy for language .' . $lang . ': .' . $e->getMessage()); | |
} | |
} | |
} | |
if (!count($this->phpMorphy)) { | |
$this->modx->log(modX::LOG_LEVEL_ERROR, '[mSearch2] Could not find any phpMorphy dictionary.'); | |
return false; | |
} | |
else { | |
return true; | |
} | |
} | |
/** | |
* Get dictionaries from phpMorphy directory | |
* | |
* @return array | |
*/ | |
public function getDictionaries() { | |
$dicts = array(); | |
$path = $this->config['dictsPath']; | |
if (!file_exists($path)) { | |
return $dicts; | |
} | |
$files = scandir($path); | |
foreach ($files as $file) { | |
if (preg_match('/\.([a-z]{2,2}\_[a-z]{2,2})\./i', $file, $matches)) { | |
$dicts[$matches[1]][] = $file; | |
} | |
} | |
return $dicts; | |
} | |
/** | |
* Returns array with words for search | |
* | |
* @param string $text | |
* @param string $pcre | |
* @param bool $with_count | |
* | |
* @return array | |
*/ | |
public function getBulkWords($text = '', $pcre = '', $with_count = false) { | |
if (empty($pcre)) { | |
$pcre = $this->config['split_words']; | |
} | |
$words = preg_split($pcre, $text, -1, PREG_SPLIT_NO_EMPTY); | |
$bulk_words = array(); | |
foreach ($words as $v) { | |
if (preg_match('/^[0-9]{2,}$/', $v) || mb_strlen($v) >= $this->config['min_word_length']) { | |
$word = mb_strtoupper($v); | |
if (!$with_count) { | |
$bulk_words[$word] = $word; | |
} | |
elseif (isset($bulk_words[$word])) { | |
$bulk_words[$word] += 1; | |
} | |
else { | |
$bulk_words[$word] = 1; | |
} | |
} | |
} | |
return $bulk_words; | |
} | |
/** | |
* Gets base form of the words | |
* | |
* @param array|string $text | |
* @param boolean $only_words | |
* | |
* @return array|string | |
*/ | |
public function getBaseForms($text, $only_words = true) { | |
$result = array(); | |
if (is_array($text)) { | |
foreach ($text as $v) { | |
$result = array_merge($result, $this->getBaseForms($v, $only_words)); | |
} | |
} | |
else { | |
$text = str_ireplace('ё', 'е', $this->modx->stripTags($text)); | |
$text = preg_replace('#\[.*\]#isU', '', $text); | |
$bulk_words = $this->getBulkWords($text, $this->config['split_all']); | |
$this->loadPhpMorphy(); | |
/* @var phpMorphy $phpMorphy */ | |
$base_forms = array(); | |
foreach ($this->phpMorphy as $phpMorphy) { | |
$locale = $phpMorphy->getLocale(); | |
$base_forms[$locale] = $phpMorphy->getBaseForm($bulk_words); | |
} | |
$result = array(); | |
foreach ($base_forms as $lang => $array) { | |
if (!empty($array)) { | |
foreach ($array as $word => $forms) { | |
if (!$forms) { | |
if (!$this->config['index_all']) { | |
continue; | |
} | |
else { | |
$forms = array($word); | |
} | |
} | |
foreach ($forms as $form) { | |
if (preg_match('/^[0-9]{2,}$/', $form) || mb_strlen($form) >= $this->config['min_word_length']) { | |
$result[$form] = $lang == 'en_EN' | |
? @iconv('WINDOWS-1250', 'UTF-8//IGNORE', $word) | |
: $word; | |
} | |
} | |
} | |
} | |
} | |
if ($only_words) { | |
$result = array_keys($result); | |
} | |
} | |
return $result; | |
} | |
/** | |
* Gets all morphological forms of the words | |
* | |
* @param array|string $text | |
* | |
* @return array|string | |
*/ | |
public function getAllForms($text) { | |
$result = array(); | |
if (is_array($text)) { | |
foreach ($text as $v) { | |
$result = array_merge($result, $this->getAllForms($v)); | |
} | |
} | |
else { | |
$text = str_ireplace('ё', 'е', $this->modx->stripTags($text)); | |
$bulk_words = $this->getBulkWords($text); | |
$this->loadPhpMorphy(); | |
/* @var phpMorphy $phpMorphy */ | |
$all_forms = array(); | |
foreach ($this->phpMorphy as $phpMorphy) { | |
$locale = $phpMorphy->getLocale(); | |
$all_forms[$locale] = $phpMorphy->getAllForms($bulk_words); | |
} | |
$result = array(); | |
if (!empty($all_forms)) { | |
foreach ($all_forms as $lang => $array) { | |
if (!empty($array)) { | |
foreach ($array as $word => $forms) { | |
if (!empty($forms)) { | |
if ($lang == 'en_EN') { | |
foreach ($forms as &$v) { | |
$v = iconv('WINDOWS-1250', 'UTF-8//IGNORE', $v); | |
} | |
} | |
$result[$word] = isset($result[$word]) | |
? array_merge($result[$word], $forms) | |
: $forms; | |
} | |
} | |
} | |
} | |
} | |
} | |
return $result; | |
} | |
/** | |
* Search and return array with resources ids as a key and sum of weight as value | |
* | |
* @param $query | |
* @param bool $process_aliases | |
* | |
* @return array | |
*/ | |
function Search($query, $process_aliases = true) { | |
if (!empty($this->config['old_search_algorithm'])) { | |
/** @noinspection PhpDeprecationInspection */ | |
return $this->OldSearch($query); | |
} | |
if ($process_aliases) { | |
$query = preg_replace('/[^_-а-яёa-z0-9\s\.\/]+/iu', ' ', $this->modx->stripTags($query)); | |
$this->log('Filtered search query: "' . mb_strtolower($query) . '"'); | |
if ($aliases = $this->getAliases($query)) { | |
$this->log('Generated aliases for search query: "' . implode('; ', $aliases) . '"'); | |
$results = array(); | |
foreach ($aliases as $query) { | |
$this->log('Search by alias: "' . $query . '"'); | |
$results[] = $this->Search($query, false); | |
} | |
$found = array(); | |
foreach ($results as $result) { | |
foreach ($result as $k => $v) { | |
if (isset($found[$k])) { | |
$found[$k] += $v; | |
} else { | |
$found[$k] = $v; | |
} | |
} | |
} | |
arsort($found); | |
return $found; | |
} | |
} | |
$search_forms = $this->getBaseForms($query, false); | |
$bulk_words = $this->getBulkWords($query); | |
// Search by words index | |
$index = $debug = array(); | |
if (!empty($search_forms)) { | |
$q = $this->modx->newQuery('mseWord'); | |
$q->select($this->modx->getSelectColumns('mseWord', 'mseWord')); | |
$q->where(array('word:IN' => array_keys($search_forms), 'field:IN' => array_keys($this->fields))); | |
$tstart = microtime(true); | |
if ($q->prepare() && $q->stmt->execute()) { | |
$this->modx->queryTime += microtime(true) - $tstart; | |
$this->modx->executedQueries++; | |
while ($row = $q->stmt->fetch(PDO::FETCH_ASSOC)) { | |
$weight = $this->fields[$row['field']] * $row['count']; | |
// Add to results | |
if (!isset($index[$row['resource']])) { | |
$index[$row['resource']] = array( | |
'words' => array( | |
$row['word'] => $weight, | |
), | |
'weight' => $weight, | |
); | |
} else { | |
if (!isset($index[$row['resource']][$row['word']])) { | |
$index[$row['resource']]['words'][$row['word']] = $weight; | |
} else { | |
$index[$row['resource']]['words'][$row['word']] += $weight; | |
} | |
$index[$row['resource']]['weight'] += $weight; | |
} | |
// Search by INDEX debug | |
$debug[$row['resource']][] = array( | |
'field' => $row['field'], | |
'word' => $row['word'], | |
'count' => $row['count'], | |
'weight' => $this->fields[$row['field']], | |
'total' => $weight, | |
); | |
} | |
} | |
} | |
$message = ''; | |
$leaved = $removed = 0; | |
$result = array(); | |
if (!empty($index)) { | |
$this->log('Found results by words INDEX (' . implode(',', $bulk_words) . '): <b>' . count($index) . '</b>'); | |
$all_forms = $this->getAllForms($bulk_words); | |
foreach ($index as $k => $v) { | |
$not_found = array(); | |
foreach ($bulk_words as $word) { | |
if (!isset($all_forms[$word])) { | |
$not_found[] = $word; | |
} elseif (!array_intersect($all_forms[$word], array_keys($v['words']))) { | |
$not_found[] = $word; | |
} | |
} | |
if (!empty($not_found)) { | |
if ($this->simpleSearch(implode(' ', $not_found), false, $k)) { | |
foreach ($not_found as $word) { | |
$index[$k]['words'][$word] = $this->config['like_match_bonus']; | |
$index[$k]['weight'] += $this->config['like_match_bonus']; | |
$message .= "\n\t+ {$this->config['like_match_bonus']} points to resource {$k} for word \"{$word}\" in LIKE search"; | |
} | |
} else { | |
unset($index[$k]); | |
$message .= "\n\t- resource {$k} because it does not contain all necessary words"; | |
$removed++; | |
continue; | |
} | |
} | |
foreach ($debug[$k] as $v2) { | |
$message .= "\n\t+ {$v2['total']} points to resource {$k} for word \"{$v2['word']}\" in field \"{$v2['field']}\" ({$v2['count']} * {$v2['weight']})"; | |
} | |
$result[$k] = $index[$k]['weight']; | |
$leaved++; | |
} | |
$this->log("Filtering results of INDEX search: <b>{$leaved}</b> leaved, <b>{$removed}</b> removed." . $message); | |
} else { | |
$this->log('Nothing found by words INDEX'); | |
} | |
if (empty($this->config['onlyIndex'])) { | |
$added = 0; | |
$like = $this->simpleSearch($query, false); | |
$message = ''; | |
foreach ($like as $k) { | |
if (!isset($result[$k])) { | |
$result[$k] = $this->config['like_match_bonus']; | |
$message .= "\n\t+ {$this->config['like_match_bonus']} points to resource {$k} for words \"" . implode(',', $bulk_words) . "\""; | |
$added++; | |
} | |
} | |
if ($added) { | |
$this->log("Added results by LIKE search: <b>{$added}</b>" . $message); | |
} | |
if (count($bulk_words) > 1) { | |
if ($exact = $this->simpleSearch($query, true, array_keys($result))) { | |
$message = 'Trying to apply "exact_match_bonus":'; | |
foreach ($exact as $k) { | |
$result[$k] += $this->config['exact_match_bonus']; | |
$message .= "\n\t+ {$this->config['exact_match_bonus']} to resource {$k}"; | |
} | |
$this->log($message); | |
} | |
} | |
} | |
// Sort results by weight | |
arsort($result); | |
// Log the search query | |
$query = preg_replace('#[^\w\s-]#iu', '', $query); | |
/** @var mseQuery $object */ | |
if ($object = $this->modx->getObject('mseQuery', array('query' => $query))) { | |
$object->set('quantity', $object->get('quantity') + 1); | |
} else { | |
$object = $this->modx->newObject('mseQuery'); | |
$object->set('query', $query); | |
$object->set('quantity', 1); | |
} | |
$object->set('found', count($result)); | |
$object->save(); | |
return $result; | |
} | |
/** | |
* Search and return array with resources ids as a key and sum of weight as value | |
* | |
* @param $query | |
* | |
* @deprecated | |
* | |
* @return array | |
*/ | |
public function OldSearch($query) { | |
$string = preg_replace('/[^_-а-яёa-z0-9\s\.\/]+/iu', ' ', $this->modx->stripTags($query)); | |
$this->log('Filtered search query: "' . mb_strtolower($query . '"')); | |
$query = $this->query = $this->addAliases($string); | |
$this->log('Search query with processed aliases: "' . mb_strtolower($query . '"')); | |
$words = $this->getBaseForms($query, false); | |
$result = $all_words = $found_words = $debug = array(); | |
// Search by words index | |
if (!empty($words)) { | |
$q = $this->modx->newQuery('mseWord'); | |
$q->select($this->modx->getSelectColumns('mseWord', 'mseWord')); | |
$q->where(array('word:IN' => array_keys($words), 'field:IN' => array_keys($this->fields))); | |
$tstart = microtime(true); | |
if ($q->prepare() && $q->stmt->execute()) { | |
$this->modx->queryTime += microtime(true) - $tstart; | |
$this->modx->executedQueries++; | |
while ($row = $q->stmt->fetch(PDO::FETCH_ASSOC)) { | |
$weight = $this->fields[$row['field']] * $row['count']; | |
// Add to results | |
if (isset($result[$row['resource']])) { | |
$result[$row['resource']] += $weight; | |
} | |
else { | |
$result[$row['resource']] = $weight; | |
} | |
// Search by INDEX debug | |
$debug[$row['resource']][] = array( | |
'field' => $row['field'], | |
'word' => $row['word'], | |
'count' => $row['count'], | |
'weight' => $this->fields[$row['field']], | |
'total' => $weight, | |
); | |
if (isset($words[$row['word']])) { | |
$all_words[$row['resource']][$words[$row['word']]] = 1; | |
$found_words[$words[$row['word']]] = 1; | |
} | |
} | |
} | |
} | |
$added = 0; | |
if (!empty($found_words)) { | |
$message = 'Found results by words INDEX (' . implode(',', array_keys($found_words)) . '): ' . count($result); | |
ksort($debug); | |
foreach ($debug as $k => $v) { | |
foreach ($v as $v2) { | |
$message .= "\n\t+ {$v2['total']} points to resource {$k} for word \"{$v2['word']}\" in field \"{$v2['field']}\" ({$v2['count']} * {$v2['weight']})"; | |
} | |
} | |
$this->log($message); | |
} | |
else { | |
$this->log('Nothing found by words INDEX'); | |
} | |
// Add bonuses | |
$bulk_words = $this->getBulkWords($query); | |
if (!empty($this->config['all_words_bonus'])) { | |
if (count($bulk_words) > 1) { | |
// All words bonus | |
foreach ($all_words as $k => $v) { | |
if (count($bulk_words) == count($v)) { | |
$result[$k] += $this->config['all_words_bonus']; | |
$this->log("+ {$this->config['all_words_bonus']} points to resource {$k} for system setting \"all_words_bonus\" with words \"" . implode(', ', $bulk_words) . "\""); | |
} | |
elseif (!empty($this->config['onlyAllWords'])) { | |
unset($result[$k]); | |
$this->log("Resource {$k} was removed because of enabled &onlyAllWords parameter with words \"" . implode(', ', array_keys($v)) . "\""); | |
} | |
} | |
} | |
} | |
if (empty($this->config['onlyIndex'])) { | |
$tmp_words = preg_split($this->config['split_words'], $query, -1, PREG_SPLIT_NO_EMPTY); | |
if (count($bulk_words) > 1 || count($tmp_words) > 1 /*|| empty($result)*/) { | |
if (!empty($this->config['exact_match_bonus']) || !empty($this->config['all_words_bonus'])) { | |
$exact = $this->simpleSearch($query); | |
// Exact match bonus | |
if (!empty($this->config['exact_match_bonus'])) { | |
foreach ($exact as $v) { | |
if (isset($result[$v])) { | |
$result[$v] += $this->config['exact_match_bonus']; | |
$this->log("+ {$this->config['exact_match_bonus']} points to resource {$v} for system setting \"exact_match_bonus\" with words \"{$query}\""); | |
} | |
else { | |
$result[$v] = $this->config['exact_match_bonus']; | |
$this->log("+ {$this->config['exact_match_bonus']} points to new resource {$v} that was found by LIKE search with exact matched words \"{$query}\""); | |
$added++; | |
} | |
} | |
} | |
} | |
} | |
elseif (!empty($this->config['like_match_bonus'])) { | |
// Add the whole possible results for query | |
$all_results = $this->simpleSearch($query); | |
foreach ($all_results as $v) { | |
if (!isset($result[$v])) { | |
$weight = round($this->config['like_match_bonus']); | |
$result[$v] = $weight; | |
$this->log("+ {$weight} points to resource {$v} by LIKE search with words \"{$query}\""); | |
$added++; | |
} | |
} | |
} | |
// Add matches by %LIKE% search | |
if (!empty($this->config['like_match_bonus']) && $not_found = array_diff($bulk_words, $words)) { | |
foreach ($not_found as $word) { | |
$found = $this->simpleSearch($word); | |
foreach ($found as $v) { | |
$weight = round($this->config['like_match_bonus']); | |
if (!isset($result[$v])) { | |
$result[$v] = $weight; | |
$this->log("+ {$weight} points to new resource {$v} that was found by LIKE search with single word \"{$word}\""); | |
$added++; | |
} | |
else { | |
$result[$v] += $weight; | |
$this->log("+ {$weight} points to resource {$v} by LIKE search for word \"{$word}\""); | |
} | |
} | |
} | |
} | |
$this->log('Added resources by LIKE search: ' . $added); | |
} | |
// Log the search query | |
$query = preg_replace('#[^\w\s-]#iu', '', $query); | |
/** @var mseQuery $object */ | |
if ($object = $this->modx->getObject('mseQuery', array('query' => $query))) { | |
$object->set('quantity', $object->get('quantity') + 1); | |
} | |
else { | |
$object = $this->modx->newObject('mseQuery'); | |
$object->set('query', $query); | |
$object->set('quantity', 1); | |
} | |
$object->set('found', count($result)); | |
$object->save(); | |
arsort($result); | |
return $result; | |
} | |
/** | |
* Search and return array with resources that matched for LIKE search | |
* | |
* @param $query | |
* @param bool $exact | |
* @param array $resources | |
* | |
* @return array | |
*/ | |
public function simpleSearch($query, $exact = true, $resources = array()) { | |
$string = $this->modx->stripTags($query); | |
$result = array(); | |
$q = $this->modx->newQuery('mseIntro'); | |
$q->select('resource'); | |
if ($exact && $string) { | |
$q->where(array('intro:LIKE' => '%' . $string . '%')); | |
} elseif ($bulk_words = $this->getBulkWords($string, $this->config['split_all'])) { | |
foreach ($bulk_words as $word) { | |
$q->andCondition("intro LIKE '%{$word}%'"); | |
} | |
} else { | |
return $result; | |
} | |
if (!empty($resources)) { | |
if (!is_array($resources)) { | |
$q->where(array('resource' => $resources)); | |
} | |
else { | |
$q->where(array('resource:IN' => $resources)); | |
} | |
} | |
$tstart = microtime(true); | |
if ($q->prepare() && $q->stmt->execute()) { | |
$this->modx->queryTime += microtime(true) - $tstart; | |
$this->modx->executedQueries++; | |
$result = $q->stmt->fetchAll(PDO::FETCH_COLUMN); | |
} | |
return $result; | |
} | |
/** | |
* @param $string | |
* | |
* @return string | |
*/ | |
public function addAliases($string) { | |
$string = mb_strtoupper(str_ireplace('ё', 'е', $this->modx->stripTags($string))); | |
$string = preg_replace('#\[.*\]#isU', '', $string); | |
$pcre = $this->config['split_words']; | |
$words = preg_split($pcre, $string, -1, PREG_SPLIT_NO_EMPTY); | |
foreach ($words as $k => $v) { | |
if (!preg_match('/^[0-9]{2,}$/', $v) && mb_strlen($v) < $this->config['min_word_length']) { | |
unset($words[$k]); | |
} | |
} | |
$words = array_unique($words); | |
$forms = $this->getBaseForms($words, false); | |
if (!$words && !$forms) { | |
return ''; | |
} | |
$q = $this->modx->newQuery('mseAlias', array('word:IN' => array_merge($words, array_keys($forms)))); | |
$q->select('word,alias,replace'); | |
$tstart = microtime(true); | |
if ($q->prepare() && $q->stmt->execute()) { | |
$this->modx->queryTime += microtime(true) - $tstart; | |
$this->modx->executedQueries++; | |
while ($row = $q->stmt->fetch(PDO::FETCH_ASSOC)) { | |
if ($row['replace']) { | |
$all = current($this->getAllForms($row['word'])); | |
$all[] = $row['word']; | |
foreach ($all as $word) { | |
$key = array_search(mb_strtoupper($word), array_map('mb_strtoupper', $words), false); | |
if ($key !== false) { | |
$words[$key] = $row['alias']; | |
break; | |
} | |
} | |
} | |
else { | |
$words[] = $row['alias']; | |
} | |
} | |
} | |
$words = array_unique(array_map('mb_strtoupper', $words)); | |
return implode(' ', $words); | |
} | |
/** | |
* @param $string | |
* | |
* @return array | |
*/ | |
public function getAliases($string) { | |
$string = mb_strtoupper(str_ireplace('ё', 'е', $this->modx->stripTags($string))); | |
$string = preg_replace('#\[.*\]#isU', '', $string); | |
$words = array_map('mb_strtoupper', array_unique($this->getBulkWords($string))); | |
$forms = $this->getBaseForms($words, false); | |
if (!$words && !$forms) { | |
return array(); | |
} | |
$aliases = array(); | |
$c = $this->modx->newQuery('mseAlias', array('word:IN' => array_merge($words, array_keys($forms)))); | |
$c->select('word, alias, replace'); | |
$c->sortby('`replace`', 'DESC'); | |
$tstart = microtime(true); | |
if ($c->prepare() && $c->stmt->execute()) { | |
$this->modx->queryTime += microtime(true) - $tstart; | |
$this->modx->executedQueries++; | |
while ($row = $c->stmt->fetch(PDO::FETCH_ASSOC)) { | |
if (!$all = current($this->getAllForms($row['word']))) { | |
$all = array($row['word']); | |
} | |
if ($row['replace']) { | |
$string = preg_replace('#\b('.implode('|', $all).')\b#iu', $row['alias'], $string); | |
} else { | |
$aliases[] = $string . ' ' . $row['alias']; | |
} | |
} | |
} | |
return array_merge(array($string), $aliases); | |
} | |
/** | |
* Highlight search string in given text string | |
* | |
* @param $text | |
* @param string $query | |
* @param string $htag_open | |
* @param string $htag_close | |
* @param boolean $strict | |
* | |
* @return mixed | |
*/ | |
public function Highlight($text, $query, $htag_open = '<b>', $htag_close = '</b>', $strict = true) { | |
if (empty($query)) { | |
return $text; | |
} | |
$from = $to = array(); | |
$tmp_words = preg_split($this->config['split_words'], $query, -1, PREG_SPLIT_NO_EMPTY); | |
// Exact match | |
$pcre = $strict | |
? '#\b' . preg_quote($query) . '\b#imus' | |
: '#' . preg_quote($query) . '#imus'; | |
if (count($tmp_words) > 1 && preg_match($pcre, $text, $matches)) { | |
$pos = mb_stripos($text, $matches[0], 0); | |
if ($pos >= $this->config['introCutBefore']) { | |
$text_cut = '... '; | |
$pos -= $this->config['introCutBefore']; | |
} | |
else { | |
$text_cut = ''; | |
$pos = 0; | |
} | |
$text_cut .= mb_substr($text, $pos, $this->config['introCutAfter']); | |
if (mb_strlen($text) > $this->config['introCutAfter']) { | |
$text_cut .= ' ...'; | |
} | |
foreach ($matches as $v) { | |
$from[$v] = $v; | |
$to[$v] = $htag_open . $v . $htag_close; | |
} | |
} | |
// Matching by separate words | |
else { | |
if (empty($this->query)) { | |
$this->query = $this->addAliases($query); | |
} | |
$tmp = array_merge( | |
$tmp_words, | |
explode(' ', $this->query) | |
); | |
$tmp = array_unique($tmp); | |
$tmp = $this->getAllForms($tmp); | |
$words = array_keys($tmp); | |
foreach ($tmp as $v) { | |
$words = array_merge($words, $v); | |
} | |
if (empty($words)) { | |
$words = array($query => $query); | |
} | |
$text_cut = ''; | |
foreach ($words as $key => $word) { | |
/* | |
if (!preg_match('/^[0-9]{2,}$/', $word) && mb_strlen($word) < $this->config['min_word_length']) { | |
unset($words[$key]); | |
continue; | |
} | |
*/ | |
$word = preg_quote($word, '/'); | |
$words[$key] = $word; | |
// Cutting text on first occurrence | |
$pcre = $strict ? '/\b' . $word . '\b/imu' : '/' . $word . '/imu'; | |
if (empty($text_cut) && !empty($word) && preg_match($pcre, $text, $matches)) { | |
$pos = mb_stripos($text, $matches[0], 0); | |
if ($pos >= $this->config['introCutBefore']) { | |
$text_cut = '... '; | |
$pos -= $this->config['introCutBefore']; | |
} | |
else { | |
$text_cut = ''; | |
$pos = 0; | |
} | |
$text_cut .= mb_substr($text, $pos, $this->config['introCutAfter']); | |
if (mb_strlen($text) > $this->config['introCutAfter']) { | |
$text_cut .= ' ...'; | |
} | |
} | |
} | |
if (empty($text_cut) && $strict) { | |
return $this->Highlight($text, $query, $htag_open, $htag_close, false); | |
} | |
$pcre = $strict ? '/\b(' . implode('|', $words) . ')\b/imu' : '/(' . implode('|', $words) . ')/imu'; | |
if (preg_match_all($pcre, $text_cut, $matches)) { | |
foreach ($matches[0] as $v) { | |
$from[$v] = $v; | |
$to[$v] = $htag_open . $v . $htag_close; | |
} | |
} | |
if (!empty($matches[1])) { | |
foreach ($matches[1] as $v) { | |
$from[$v] = $v; | |
$to[$v] = $htag_open . $v . $htag_close; | |
} | |
} | |
elseif ($strict) { | |
return $this->Highlight($text, $query, $htag_open, $htag_close, false); | |
} | |
} | |
return str_replace($from, $to, $text_cut); | |
} | |
/** | |
* Return array with filters | |
* | |
* @param array|string $ids | |
* @param boolean $build | |
* | |
* @return array|boolean | |
*/ | |
public function getFilters($ids, $build = true) { | |
// prepare ids | |
if (!is_array($ids)) { | |
$ids = array_map('trim', explode(',', $ids)); | |
} | |
if (empty($ids)) { | |
return false; | |
} | |
// Return results from cache | |
if ($build && $cache = $this->modx->cacheManager->get('msearch2/prep_' . md5(implode(',', $ids) . $this->config['filters']))) { | |
$this->methods = $cache['methods']; | |
$this->aliases = $cache['aliases']; | |
return $cache['filters']; | |
} | |
// elseif (!$build && !empty($this->filters)) { | |
// return $this->filters; | |
// } | |
elseif (!$build && $cache = $this->modx->cacheManager->get('msearch2/prep_' . md5(implode(',', $ids) . $this->config['filters']))) { | |
$this->methods = $cache['methods']; | |
$this->aliases = $cache['aliases']; | |
$filters = $cache['filters']; | |
$filters2 = []; | |
foreach($filters as $k=>$v){ | |
@list($table, $filter) = explode($this->config['filter_delimeter'], $k); | |
foreach($v as $k2=>$v2){ | |
$filters2[$table][$filter][$k2] = $v2['resources']; | |
} | |
} | |
$this->filters = $filters2; | |
unset($filters);unset($filters2); | |
return $this->filters; | |
} | |
// elseif (!$build && $cache = $this->modx->cacheManager->get('msearch2/fltr_' . md5(implode(',', $ids) . $this->config['filters']))) { | |
// $this->methods = $cache['methods']; | |
// $this->aliases = $cache['aliases']; | |
// return $cache['filters']; | |
// } | |
if (!is_object($this->filtersHandler)) { | |
$this->loadHandler(); | |
} | |
if (empty($this->filters) || empty($this->methods)) { | |
// Preparing filters | |
$filters = $built = $duplicates = array(); | |
$tmp_filters = array_map('trim', explode(',', $this->config['filters'])); | |
foreach ($tmp_filters as $v) { | |
$v = strtolower($v); | |
if (empty($v)) { | |
continue; | |
} | |
elseif (strpos($v, $this->config['filter_delimeter']) !== false) { | |
@list($table, $filter) = explode($this->config['filter_delimeter'], $v); | |
} | |
else { | |
$table = 'resource'; | |
$filter = $v; | |
} | |
$tmp = explode($this->config['method_delimeter'], $filter); | |
$name = $tmp[0]; | |
$filter = !empty($tmp[1]) | |
? $tmp[1] | |
: 'default'; | |
// Duplicates | |
if (isset($filters[$table][$name])) { | |
$old_filter = $built[$table . $this->config['filter_delimeter'] . $name]; | |
$new_name = $name . '-' . $old_filter; | |
$built[$table . $this->config['filter_delimeter'] . $new_name] = $old_filter; | |
$filters[$table][$new_name] = $filters[$table][$name]; | |
$duplicates[$table][$new_name] = $name; | |
$new_name = $name . '-' . $filter; | |
$duplicates[$table][$new_name] = $name; | |
$name = $new_name; | |
} | |
$filters[$table][$name] = array(); | |
$built[$table . $this->config['filter_delimeter'] . $name] = $filter; | |
} | |
// Retrieving filters | |
foreach ($filters as $table => &$fields) { | |
$method = 'get' . ucfirst($table) . 'Values'; | |
$keys = !empty($duplicates[$table]) | |
? array_diff(array_keys($fields), array_keys($duplicates[$table])) | |
: array_keys($fields); | |
if (method_exists($this->filtersHandler, $method)) { | |
$fields = call_user_func_array(array($this->filtersHandler, $method), array($keys, $ids)); | |
if (!empty($duplicates[$table])) { | |
foreach ($duplicates[$table] as $key => $field) { | |
$fields[$key] = $fields[$field]; | |
} | |
} | |
} | |
else { | |
$this->modx->log(modX::LOG_LEVEL_ERROR, '[mSearch2] Method "' . $method . '" not exists in class "' . get_class($this->filtersHandler) . '". Could not retrieve filters from "' . $table . '"'); | |
} | |
} | |
unset($fields); | |
// Remove duplicates | |
foreach ($duplicates as $table => $fields) { | |
$tmp = array_unique($fields); | |
foreach ($tmp as $tmp2) { | |
unset($filters[$table][$tmp2]); | |
unset($built[$table . $this->config['filter_delimeter'] . $tmp2]); | |
} | |
} | |
// Prepare aliases | |
$aliases = array(); | |
if (!empty($this->config['aliases'])) { | |
$tmp = array_map('trim', explode(',', $this->config['aliases'])); | |
foreach ($tmp as $v) { | |
if (strpos($v, '==') !== false) { | |
$tmp2 = array_map('trim', explode('==', $v)); | |
$aliases[str_replace('.', '_', $tmp2[0])] = $tmp2[1]; | |
} | |
} | |
$this->aliases = $aliases; | |
} | |
$this->filters = $filters; | |
$this->methods = $built; | |
} | |
// The replacement dots to underscores in filters names | |
foreach ($this->filters as $table => $fields) { | |
foreach ($fields as $key => $values) { | |
if (strpos($key, '.') !== false) { | |
$this->filters[$table][str_replace('.', '_', $key)] = $values; | |
unset($this->filters[$table][$key]); | |
} | |
} | |
} | |
if (!$build) { | |
return $this->filters; | |
} | |
$built = $this->methods; | |
$prepared = array(); | |
foreach ($this->filters as $table => $filters) { | |
foreach ($filters as $key => $values) { | |
$new_key = $table . $this->config['filter_delimeter'] . $key; | |
$filter = !empty($built[$new_key]) | |
? $built[$new_key] | |
: 'default'; | |
$method = 'build' . ucfirst($filter) . 'Filter'; | |
if ($filter == 'default') { | |
switch ($table) { | |
case 'tv': | |
$method = 'buildTVsFilter'; | |
break; | |
case 'msoption': | |
$method = 'buildOptionsFilter'; | |
break; | |
} | |
} | |
if (method_exists($this->filtersHandler, $method)) { | |
$prepared[$new_key] = call_user_func_array(array($this->filtersHandler, $method), array($values, $key)); | |
} | |
elseif (method_exists($this->filtersHandler, 'buildDefaultFilter')) { | |
$prepared[$new_key] = call_user_func_array(array($this->filtersHandler, 'buildDefaultFilter'), array($values, $key)); | |
} | |
else { | |
$this->modx->log(modX::LOG_LEVEL_ERROR, '[mSearch2] Method "' . $method . '" not exists in class "' . get_class($this->filtersHandler) . '". Could not build filter for "' . $new_key . '"'); | |
$prepared[$new_key] = $values; | |
} | |
} | |
} | |
// Sort filters | |
foreach ($built as $key => &$values) { | |
if (isset($prepared[$key])) { | |
$values = $prepared[$key]; | |
unset($prepared[$key]); | |
} | |
} | |
// Add new generated filters to the end of list | |
$built = array_merge($built, $prepared); | |
$cache = array( | |
'filters' => $built, | |
'methods' => $this->methods, | |
'aliases' => $this->aliases, | |
); | |
// Set cache | |
if (!empty($this->config['cacheTime'])) { | |
$this->modx->cacheManager->set('msearch2/prep_' . md5(implode(',', $ids) . $this->config['filters']), $cache, $this->config['cacheTime']); | |
} | |
return $built; | |
} | |
/** | |
* Filters resources by given params | |
* | |
* @param array|string $ids | |
* @param array $request | |
* | |
* @return array | |
*/ | |
public function Filter($ids, array $request) { | |
if (!is_array($ids)) { | |
$ids = explode(',', $ids); | |
} | |
if (!is_object($this->filtersHandler)) { | |
$this->loadHandler(); | |
} | |
//$this->pdoTools->addTime('Getting filters2 for '.count($ids).' ids'); | |
$this->getFilters($ids, false); | |
//$this->pdoTools->addTime('End Getting filters2 for '.count($ids).' ids'); | |
$filters = $this->filters; | |
$methods = $this->methods; | |
$aliases = array_flip($this->aliases); | |
//$this->pdoTools->addTime('End Getting filters2 for '.print_r($filters,1).' ids'); | |
//$this->pdoTools->addTime('ids1'); | |
foreach ($request as $filter => $requested) { | |
if (!empty($aliases[$filter])) { | |
$filter = $aliases[$filter]; | |
} | |
if (!preg_match('/(.*?)' . preg_quote($this->config['filter_delimeter'], '/') . '(.*?)/', $filter)) { | |
continue; | |
} | |
$method = !empty($methods[$filter]) | |
? 'filter' . ucfirst($methods[$filter]) | |
: 'filterDefault'; | |
list($table, $filter) = explode($this->config['filter_delimeter'], $filter); | |
//$this->pdoTools->addTime("table $table filter $filter ".print_r($filters,1)); | |
if (isset($filters[$table][$filter])) { | |
$values = $filters[$table][$filter]; | |
$requested = array_map('rawurldecode', explode($this->config['values_delimeter'], $requested)); | |
if (method_exists($this->filtersHandler, $method)) { | |
$ids = call_user_func_array(array($this->filtersHandler, $method), array($requested, $values, $ids)); | |
//$this->pdoTools->addTime('ids2'); | |
} | |
else { | |
//$this->modx->log(modX::LOG_LEVEL_ERROR, '[mSearch2] Method "'.$method.'" not exists in class "'.get_class($this->filtersHandler).'". Could not build filter "'.$table.$this->config['filter_delimeter'].$filter.'"'); | |
$ids = @call_user_func_array(array($this->filtersHandler, 'filterDefault'), array($requested, $values, $ids)); | |
//$this->pdoTools->addTime('ids21'); | |
} | |
} | |
$this->filter_operations++; | |
} | |
//$this->pdoTools->addTime('ids3 '.print_r($ids,1).' ids'); | |
return $ids; | |
} | |
/** | |
* This method returns preliminary results for each filter | |
* | |
* @param $ids | |
* @param array $request | |
* @param array $current | |
* | |
* @return array | |
*/ | |
public function getSuggestions($ids, array $request, array $current = array()) { | |
if (!is_array($ids)) { | |
$ids = explode(',', $ids); | |
} | |
if (!is_object($this->filtersHandler)) { | |
$this->loadHandler(); | |
} | |
foreach ($request as $k => $v) { | |
$request[$k] = str_replace('"', '"', $v); | |
} | |
if (method_exists($this->filtersHandler, 'getSuggestions')) { | |
return $this->filtersHandler->getSuggestions($ids, $request, $current); | |
} | |
else { | |
$current = array_flip($current); | |
$filters = $this->getFilters($ids, false); | |
$built = $this->getFilters($ids, true); | |
$radio = $this->config['suggestionsRadio']; | |
$aliases = $this->aliases; | |
$suggestions = array(); | |
foreach ($filters as $table => $fields) { | |
foreach ($fields as $field => $values) { | |
$key = $alias = $table . $this->config['filter_delimeter'] . $field; | |
if (!empty($aliases[$key])) { | |
$alias = $aliases[$key]; | |
} | |
$tmp = current($built[$key]); | |
if (empty($tmp['type']) || $tmp['type'] != 'number') { | |
$tmp = $built[$key]; | |
$values = array(); | |
foreach ($tmp as $v) { | |
$values[] = $v['value']; | |
} | |
} elseif (!empty($this->config['suggestionsSliders'])) { | |
$values = array_keys($values); | |
} else { | |
continue; | |
} | |
foreach ($values as $value) { | |
$suggest = $request; | |
$added = 0; | |
if (isset($request[$alias])) { | |
// Types of suggestion can depend from method | |
if (!empty($radio) && in_array($key, $radio)) { | |
$suggest[$alias] = $value; | |
} elseif ($tmp['type'] == 'number') { | |
$tmp2 = explode($this->config['values_delimeter'], $request[$alias]); | |
if ($value <= $tmp2[0]) { | |
$tmp2[0] = $value; | |
} else { | |
$tmp2[1] = $value; | |
} | |
$suggest[$alias] = implode($this->config['values_delimeter'], $tmp2); | |
} else { | |
$tmp2 = explode($this->config['values_delimeter'], $request[$alias]); | |
if (!in_array($value, $tmp2)) { | |
$suggest[$alias] .= $this->config['values_delimeter'] . $value; | |
$added = 1; | |
} | |
} | |
$res = $this->Filter($ids, $suggest); | |
if ($added && !empty($res)) { | |
foreach ($res as $k => $v) { | |
if (isset($current[$v])) { | |
unset($res[$k]); | |
} | |
} | |
$count = count($res); | |
if ($count != 0) { | |
$count = '+' . $count; | |
} | |
} | |
else { | |
$count = count($res); | |
} | |
} | |
else { | |
$suggest[$alias] = $value; | |
$res = $this->Filter($ids, $suggest); | |
$count = count($res); | |
} | |
$suggestions[$alias][$value] = $count; | |
} | |
} | |
} | |
return $suggestions; | |
} | |
} | |
/** | |
* Method for transform array to placeholders | |
* | |
* @var array $array With keys and values | |
* @var string $prefix | |
* | |
* @return array $array Two nested arrays With placeholders and values | |
*/ | |
public function makePlaceholders(array $array = array(), $prefix = '') { | |
$result = array( | |
'pl' => array(), | |
'vl' => array(), | |
); | |
foreach ($array as $k => $v) { | |
if (is_array($v)) { | |
$result = array_merge_recursive($result, $this->makePlaceholders($v, $prefix . $k . '.')); | |
} | |
else { | |
$result['pl'][$prefix . $k] = '[[+' . $prefix . $k . ']]'; | |
$result['vl'][$prefix . $k] = $v; | |
} | |
} | |
return $result; | |
} | |
/** | |
* Returns string for insert into sorting properties of pdoTools snippet | |
* | |
* @param string | |
* | |
* @return string | |
*/ | |
public function getSortFields($sort) { | |
$this->loadHandler(); | |
return $this->filtersHandler->getSortFields($sort); | |
} | |
/** | |
* Loads custom filters handler class | |
* | |
* @return bool | |
*/ | |
public function loadHandler() { | |
if (!is_object($this->filtersHandler)) { | |
require_once 'filters.class.php'; | |
$filters_class = $this->modx->getOption('mse2_filters_handler_class', null, 'mse2FiltersHandler', true); | |
if ($filters_class != 'mse2FiltersHandler') { | |
$this->loadCustomClasses('filters'); | |
} | |
if (!class_exists($filters_class)) { | |
$filters_class = 'mse2FiltersHandler'; | |
} | |
$this->filtersHandler = new $filters_class($this, $this->config); | |
if (!($this->filtersHandler instanceof mse2FiltersHandler)) { | |
$this->modx->log(modX::LOG_LEVEL_ERROR, '[mSearch2] Could not initialize filters handler class: "' . $filters_class . '"'); | |
return false; | |
} | |
} | |
return true; | |
} | |
/** | |
* Checks for exists of miniShop2 | |
* | |
* @return bool | |
*/ | |
public function checkMS2() { | |
return file_exists(MODX_CORE_PATH . 'components/minishop2/model/minishop2/msproduct.class.php'); | |
} | |
/** | |
* @param $entry | |
*/ | |
public function log($entry) { | |
if ($this->pdoTools && !empty($this->config['showSearchLog'])) { | |
$this->pdoTools->addTime('[mSearch2] ' . $entry); | |
} | |
} | |
/** | |
* | |
*/ | |
protected function checkStat() { | |
} | |
} |
Заменил только эту функцию. Ничего не поменялось. Печаль. Он так ускорял работу(
Пишите мне на скайп touols или на почту touols@yandex.ru. Посмотрим что у вас не работает. Только это платно 600р час.
Вчера применял решение на сайт с 50кило товаров. Выяснилось несколько недоработок. Наверно "фильтры типа boolean и типа vendors" в итоге правок будут работать. Но мне не охота отдавать решение бесплатно. Был бы mFilter2 беслатным, то вопросов нет давно бы сделал пул реквест. А так жаба давит. Я тут напрягался дебажил придумывал, а разрабы, если опубликую решение, к себе применят и будут компонент еще лучше продовать деньги зашибать :-).
Всем кому нужно, предлогаю, 1000р решение включая 20минут применить и 600р в час оптимизация страниц каталога.
Например, вчера в чанке выявился сниппет на 6-10с грузящий страницу. Его тоже закешировал и страница каталога на 3500 товаров стала выдаваться за 1,5 секунды вместо 25с.
Если вдруг разрабы mSearch2 захотят приобрести решение, то продам за 15 000р :-)
Напишу в скайпе
Если вдруг разрабы mSearch2 захотят приобрести решение, то продам за 15 000р :-)
@touol Привет! Так запости может краудфандинг или типа того на modx.pro, а то блин есть проблема с платными компонентами что если автор не добавляет фиксов, то их вообще нет)
Мои правки только в функции public function getFilters($ids, $build = true) {
А сам файл может быть от другой версии mSearch2 и при полном копировании может вызывать ошибки. Попробуйте только функцию скопировать.
И при вызове mFilter2 ставить &cacheTime число больше 0.