yf_related_content class

Code for searching related content.

Fileplugins/common/classes/common/yf_related_content.class.php
Use_class('related_content', 'classes/common/')

Public properties

A site sets them in its config with $PROJECT_CONF['related_content']['NAME'] = ...;

PropertyDefaultDescription
$DEF_PARAMS
@conf_skip
['WHAT_TO_RETURN' => 'sql', 'FIELD_ID' => 'id', 'FIELD_DATE' => 'add_date', 'FIELD_USER' => 'user_id', 'FIELD_TITLE' => 'title', 'FIELD_TEXT' => 'text', ... (19 items)]
array (
  'WHAT_TO_RETURN' => 'sql',
  'FIELD_ID' => 'id',
  'FIELD_DATE' => 'add_date',
  'FIELD_USER' => 'user_id',
  'FIELD_TITLE' => 'title',
  'FIELD_TEXT' => 'text',
  'FIELD_SCORE' => 'score',
  'FIELD_ADD_1' => '',
  'FIELD_ADD_2' => '',
  'PAST_ONLY' => 0,
  'RECORDS_LIMIT' => 5,
  'THRESHOLD' => 1,
  'WEIGHT_TEXT' => 3,
  'WEIGHT_TITLE' => 1,
  'WEIGHT_TAG' => 0,
  'WEIGHT_CAT' => 0,
  'WEIGHT_ADD_1' => 1,
  'WEIGHT_ADD_2' => 1,
  'STPL_NAME' => 'system/common/related_content',
)
$NUM_KEYWORDS
@conf_skip
20
$STOP_WORDS
List
['', 'a', 'an', 'the', 'and', 'of', ... (230 items)]
array (
  0 => '',
  1 => 'a',
  2 => 'an',
  3 => 'the',
  4 => 'and',
  5 => 'of',
  6 => 'i',
  7 => 'to',
  8 => 'is',
  9 => 'in',
  10 => 'with',
  11 => 'for',
  12 => 'as',
  13 => 'that',
  14 => 'on',
  15 => 'at',
  16 => 'this',
  17 => 'my',
  18 => 'was',
  19 => 'our',
  20 => 'it',
  21 => 'you',
  22 => 'we',
  23 => '1',
  24 => '2',
  25 => '3',
  26 => '4',
  27 => '5',
  28 => '6',
  29 => '7',
  30 => '8',
  31 => '9',
  32 => '0',
  33 => '10',
  34 => 'about',
  35 => 'after',
  36 => 'all',
  37 => 'almost',
  38 => 'along',
  39 => 'also',
  40 => 'amp',
  41 => 'another',
  42 => 'any',
  43 => 'are',
  44 => 'area',
  45 => 'around',
  46 => 'available',
  47 => 'back',
  48 => 'be',
  49 => 'because',
  50 => 'been',
  51 => 'being',
  52 => 'best',
  53 => 'better',
  54 => 'big',
  55 => 'bit',
  56 => 'both',
  57 => 'but',
  58 => 'by',
  59 => 'c',
  60 => 'came',
  61 => 'can',
  62 => 'capable',
  63 => 'control',
  64 => 'could',
  65 => 'course',
  66 => 'd',
  67 => 'dan',
  68 => 'day',
  69 => 'decided',
  70 => 'did',
  71 => 'didn',
  72 => 'different',
  73 => 'div',
  74 => 'do',
  75 => 'doesn',
  76 => 'don',
  77 => 'down',
  78 => 'drive',
  79 => 'e',
  80 => 'each',
  81 => 'easily',
  82 => 'easy',
  83 => 'edition',
  84 => 'end',
  85 => 'enough',
  86 => 'even',
  87 => 'every',
  88 => 'example',
  89 => 'few',
  90 => 'find',
  91 => 'first',
  92 => 'found',
  93 => 'from',
  94 => 'get',
  95 => 'go',
  96 => 'going',
  97 => 'good',
  98 => 'got',
  99 => 'gt',
  100 => 'had',
  101 => 'hard',
  102 => 'has',
  103 => 'have',
  104 => 'he',
  105 => 'her',
  106 => 'here',
  107 => 'how',
  108 => 'if',
  109 => 'into',
  110 => 'isn',
  111 => 'just',
  112 => 'know',
  113 => 'last',
  114 => 'left',
  115 => 'li',
  116 => 'like',
  117 => 'little',
  118 => 'll',
  119 => 'long',
  120 => 'look',
  121 => 'lot',
  122 => 'lt',
  123 => 'm',
  124 => 'made',
  125 => 'make',
  126 => 'many',
  127 => 'mb',
  128 => 'me',
  129 => 'menu',
  130 => 'might',
  131 => 'mm',
  132 => 'more',
  133 => 'most',
  134 => 'much',
  135 => 'name',
  136 => 'nbsp',
  137 => 'need',
  138 => 'new',
  139 => 'no',
  140 => 'not',
  141 => 'now',
  142 => 'number',
  143 => 'off',
  144 => 'old',
  145 => 'one',
  146 => 'only',
  147 => 'or',
  148 => 'original',
  149 => 'other',
  150 => 'out',
  151 => 'over',
  152 => 'part',
  153 => 'place',
  154 => 'point',
  155 => 'pretty',
  156 => 'probably',
  157 => 'problem',
  158 => 'put',
  159 => 'quite',
  160 => 'quot',
  161 => 'r',
  162 => 're',
  163 => 'really',
  164 => 'results',
  165 => 'right',
  166 => 's',
  167 => 'same',
  168 => 'saw',
  169 => 'see',
  170 => 'set',
  171 => 'several',
  172 => 'she',
  173 => 'sherree',
  174 => 'should',
  175 => 'since',
  176 => 'size',
  177 => 'small',
  178 => 'so',
  179 => 'some',
  180 => 'something',
  181 => 'special',
  182 => 'still',
  183 => 'stuff',
  184 => 'such',
  185 => 'sure',
  186 => 'system',
  187 => 't',
  188 => 'take',
  189 => 'than',
  190 => 'their',
  191 => 'them',
  192 => 'then',
  193 => 'there',
  194 => 'these',
  195 => 'they',
  196 => 'thing',
  197 => 'things',
  198 => 'think',
  199 => 'those',
  200 => 'though',
  201 => 'through',
  202 => 'time',
  203 => 'today',
  204 => 'together',
  205 => 'too',
  206 => 'took',
  207 => 'two',
  208 => 'up',
  209 => 'us',
  210 => 'use',
  211 => 'used',
  212 => 'using',
  213 => 've',
  214 => 'very',
  215 => 'want',
  216 => 'way',
  217 => 'well',
  218 => 'went',
  219 => 'were',
  220 => 'what',
  221 => 'when',
  222 => 'where',
  223 => 'which',
  224 => 'while',
  225 => 'white',
  226 => 'who',
  227 => 'will',
  228 => 'would',
  229 => 'your',
)
of words to skip when analyzing @conf_skip
$_PATTERN_KWORDS
@conf_skip
'/\\s*[\\s+\\.|\\?|,|(|)|\\-+|'|\\"|=|;|×|$|\\/|:|{|}]\\s*/i'
$num_to_ret null

Public methods 3

None without a leading underscore.

Methods starting with _ hooks and helpers; not routable as page actions

public function _get_keywords_from_text($text = '')

Analyze text, find top keywords and return string ready for use inside 'MATCH ... AGAINST ...'.

    public function _get_keywords_from_text($text = '')
    {
        $num_to_ret = $this->NUM_KEYWORDS;

        $wordlist = preg_split($this->_PATTERN_KWORDS, _strtolower(strip_tags($text)));

        // Build an array of the unique words and number of times they occur.
        $a = array_count_values($wordlist);

        // Remove the stop words from the list.
        foreach ((array) $this->STOP_WORDS as $_word) {
            unset($a[$_word]);
        }

        // Remove short words from the list.
        foreach ((array) $a as $k => $v) {
            if (_strlen($k) < 4) {
                unset($a[$k]);
            }
        }
        arsort($a, SORT_NUMERIC);

        $num_words = count((array) $a);
        $num_to_ret = $num_words > $num_to_ret ? $num_to_ret : $num_words;

        $outwords = array_slice($a, 0, $num_to_ret);
        $result = implode(' ', array_keys($outwords));
        return $result;
    }
public function _prepare_fields_param($fields = '')

Cleanup and convert fields param value into array.

    public function _prepare_fields_param($fields = '')
    {
        if (is_array($fields)) {
            return array_unique($fields);
        }
        if ( ! is_string($fields)) {
            return [];
        }
        $fields_array = [];
        $fields = strtolower(preg_replace('/[^a-z0-9_,]/i', '', trim($fields)));
        foreach ((array) explode(',', $fields) as $_item) {
            $fields_array[$_item] = $_item;
        }
        return $fields_array;
    }

_process

public function _process($params = [])

Get related content.

    public function _process($params = [])
    {
        // THESE ARE REQUIRED!
        $SOURCE_ARRAY = $params['source_array'];
        $TABLE_NAME = $params['table_name'];
        // Missing required params
        if ( ! $SOURCE_ARRAY) {
            trigger_error('RELATED: empty params[source_array]', E_USER_WARNING);
            return false;
        }
        $WHAT_TO_RETURN = $params['action'] && in_array($params['action'], ['sql', 'fetch', 'stpl']) ? $params['action'] : $this->DEF_PARAMS['WHAT_TO_RETURN'];

        $FIELD_ID = $params['field_id'] ? _es($params['field_id']) : $this->DEF_PARAMS['FIELD_ID'];
        $FIELD_DATE = $params['field_date'] ? _es($params['field_date']) : $this->DEF_PARAMS['FIELD_DATE'];
        $FIELD_USER = $params['field_user'] ? _es($params['field_user']) : $this->DEF_PARAMS['FIELD_USER'];
        $FIELD_TITLE = $params['field_title'] ? _es($params['field_title']) : $this->DEF_PARAMS['FIELD_TITLE'];
        $FIELD_TEXT = $params['field_text'] ? _es($params['field_text']) : $this->DEF_PARAMS['FIELD_TEXT'];
        $FIELD_SCORE = $params['field_score'] ? _es($params['field_score']) : $this->DEF_PARAMS['FIELD_SCORE'];
        // Additional fields for fulltext searching
        $FIELD_ADD_1 = $params['field_add_1'] ? _es($params['field_add_1']) : $this->DEF_PARAMS['FIELD_ADD_1'];
        $FIELD_ADD_2 = $params['field_add_2'] ? _es($params['field_add_2']) : $this->DEF_PARAMS['FIELD_ADD_2'];
        // Title or text is required
        if (( ! $FIELD_TITLE || $FIELD_TITLE == -1) && ( ! $FIELD_TEXT || $FIELD_TEXT == -1)) {
            trigger_error('RELATED: no title and text fields specified', E_USER_WARNING);
            return false;
        }
        $FIELDS_RETURN = $params['fields_return'] ? $this->_prepare_fields_param($params['fields_return']) : '';
        if ( ! $FIELDS_RETURN) {
            $FIELDS_RETURN[] = $FIELD_ID;
            if ($FIELD_DATE && $FIELD_DATE != -1) {
                $FIELDS_RETURN[] = $FIELD_DATE;
            }
            if ($FIELD_USER && $FIELD_USER != -1) {
                $FIELDS_RETURN[] = $FIELD_USER;
            }
            if ($FIELD_TITLE && $FIELD_TITLE != -1) {
                $FIELDS_RETURN[] = $FIELD_TITLE;
            }
            if ($FIELD_TEXT && $FIELD_TEXT != -1) {
                $FIELDS_RETURN[] = $FIELD_TEXT;
            }
            if ($FIELD_ADD_1 && $FIELD_ADD_1 != -1) {
                $FIELDS_RETURN[] = $FIELD_ADD_1;
            }
            if ($FIELD_ADD_2 && $FIELD_ADD_2 != -1) {
                $FIELDS_RETURN[] = $FIELD_ADD_2;
            }
            $FIELDS_RETURN[] = $FIELD_SCORE;
        }

        $WHERE_COND = $params['where'] ? $params['where'] : ''; // Not checked. Be careful with this!
        $PAST_ONLY = $params['past_only'] ? (int) ((bool) $params['past_only']) : $this->DEF_PARAMS['PAST_ONLY'];
        if ( ! $FIELD_DATE || $FIELD_DATE == -1) {
            $PAST_ONLY = false;
        }
        $RECORDS_LIMIT = $params['limit'] ? (int) ($params['limit']) : $this->DEF_PARAMS['RECORDS_LIMIT'];
        $ORDER_BY = $params['order_by'] ? _es($params['order_by']) : $FIELD_SCORE . ' DESC';
        $STPL_NAME = $params['stpl_name'] ? $params['stpl_name'] : $this->DEF_PARAMS['STPL_NAME'];

        $THRESHOLD = $params['thold'] ? (int) ($params['thold']) : $this->DEF_PARAMS['THRESHOLD'];
        $WEIGHT_TEXT = $params['weight_body'] ? (int) ($params['weight_body']) : $this->DEF_PARAMS['WEIGHT_TEXT'];
        $WEIGHT_TITLE = $params['weight_title'] ? (int) ($params['weight_title']) : $this->DEF_PARAMS['WEIGHT_TITLE'];
        // Additional fields
        $WEIGHT_ADD_1 = $params['weight_add_1'] ? (int) ($params['weight_add_1']) : $this->DEF_PARAMS['WEIGHT_ADD_1'];
        $WEIGHT_ADD_2 = $params['weight_add_2'] ? (int) ($params['weight_add_2']) : $this->DEF_PARAMS['WEIGHT_ADD_2'];
        // TODO: complete these
        $WEIGHT_TAG = $params['weight_tag'] ? (int) ($params['weight_tag']) : $this->DEF_PARAMS['WEIGHT_TAG'];
        $WEIGHT_CAT = $params['weight_cat'] ? (int) ($params['weight_cat']) : $this->DEF_PARAMS['WEIGHT_CAT'];

        // PARSE PARAMS END

        $WEIGHT_TOTAL = $WEIGHT_TEXT + $WEIGHT_TITLE + $WEIGHT_TAG + $WEIGHT_CAT;
        $WEIGHTED_THOLD = $THRESHOLD / ($WEIGHT_TOTAL + 0.1);

        $keywords_text = '';
        if ($FIELD_TEXT && $FIELD_TEXT != -1) {
            $keywords_text = $this->_get_keywords_from_text($SOURCE_ARRAY[$FIELD_TEXT]);
        }
        $keywords_title = '';
        if ($FIELD_TITLE && $FIELD_TITLE != -1) {
            $keywords_title = $this->_get_keywords_from_text($SOURCE_ARRAY[$FIELD_TITLE]);
        }
        $keywords_add_1 = '';
        if ($FIELD_ADD_1 && $FIELD_ADD_1 != -1) {
            $keywords_add_1 = $this->_get_keywords_from_text($SOURCE_ARRAY[$FIELD_ADD_1]);
        }
        $keywords_add_2 = '';
        if ($FIELD_ADD_2 && $FIELD_ADD_2 != -1) {
            $keywords_add_2 = $this->_get_keywords_from_text($SOURCE_ARRAY[$FIELD_ADD_2]);
        }
        // Keywords required
        if ( ! strlen($keywords_text) && ! strlen($keywords_title) && ! strlen($keywords_add_1) && ! strlen($keywords_add_2)) {
            return false;
        }
        // Prepare fields to return as string for SQL
        $_tmp = [];
        foreach ((array) $FIELDS_RETURN as $k => $v) {
            $_tmp[$k] = db()->escape_key($v);
        }
        $fields_to_return_sql = implode(', ', $_tmp);
        unset($_tmp);

        $now = time();

        // TODO
        $cats = '';
        $tags = '';

        $sql =
            'SELECT *, ( 
				score_text	* ' . $WEIGHT_TEXT . ' 
				' . (strlen($keywords_title) ? ' + score_title	* ' . $WEIGHT_TITLE : '') . '
				' . (strlen($keywords_add_1) ? ' + score_add_1	* ' . $WEIGHT_ADD_1 : '') . '
				' . (strlen($keywords_add_2) ? ' + score_add_2	* ' . $WEIGHT_ADD_2 : '') . '
				' . ($tags ? ' + score_tag	* ' . $WEIGHT_TAG : '') . '
				' . ($cats ? ' + score_cat	* ' . $WEIGHT_CAT : '') . '
			) AS ' . $FIELD_SCORE . ' 

			FROM ( 
				SELECT ' . ($fields_to_return_sql ? $fields_to_return_sql : '1') . '
					, ' . (strlen($keywords_text) ? '(MATCH (' . $FIELD_TEXT . ") AGAINST ('" . _es($keywords_text) . "' IN BOOLEAN MODE))" : '0') . ' AS score_text 
					' . (strlen($keywords_title) ? ', (MATCH (' . $FIELD_TITLE . ") AGAINST ('" . _es($keywords_title) . "' IN BOOLEAN MODE)) AS score_title " : '') . '
					' . (strlen($keywords_add_1) ? ', (MATCH (' . $FIELD_ADD_1 . ") AGAINST ('" . _es($keywords_add_1) . "' IN BOOLEAN MODE)) AS score_add_1 " : '') . '
					' . (strlen($keywords_add_2) ? ', (MATCH (' . $FIELD_ADD_2 . ") AGAINST ('" . _es($keywords_add_2) . "' IN BOOLEAN MODE)) AS score_add_2 " : '') . '
					' . ($tags ? ', IFNULL(0/*score_tag*/,0) AS score_tag ' : '') . '
					' . ($cats ? ', IFNULL(0/*score_cat*/,0) as score_cat ' : '') . '
				FROM ' . $TABLE_NAME . ' 
				WHERE ' . ($WHERE_COND ? $WHERE_COND : '1') . ' 
					AND ' . $FIELD_ID . ' != ' . (int) ($SOURCE_ARRAY[$FIELD_ID])
                    . ($PAST_ONLY ? ' AND ' . $FIELD_DATE . " <= '" . $now . "' " : ' ')
            . ') AS rawscores 

			WHERE ( 
				score_text	* ' . $WEIGHT_TEXT . '
				' . (strlen($keywords_title) ? ' + score_title	* ' . $WEIGHT_TITLE : '') . '
				' . (strlen($keywords_add_1) ? ' + score_add_1 * ' . $WEIGHT_ADD_1 : '') . '
				' . (strlen($keywords_add_2) ? ' + score_add_2 * ' . $WEIGHT_ADD_2 : '') . '
				' . ($tags ? ' + score_tag	* ' . $WEIGHT_TAG : '') . '
				' . ($cats ? ' + score_cat	* ' . $WEIGHT_CAT : '') . '
			) >= ' . $THRESHOLD . '

			ORDER BY ' . $ORDER_BY . ' 

			LIMIT ' . $RECORDS_LIMIT;

        // Special for the db_installer (allows to easily restore if not exists FULLTEXT INDEX on used fields)
        $fulltext_needed_for = [];
        if (strlen($keywords_text)) {
            $fulltext_needed_for[] = $TABLE_NAME . '.' . $FIELD_TEXT;
        }
        if (strlen($keywords_title)) {
            $fulltext_needed_for[] = $TABLE_NAME . '.' . $FIELD_TITLE;
        }
        if (strlen($keywords_add_1)) {
            $fulltext_needed_for[] = $TABLE_NAME . '.' . $FIELD_ADD_1;
        }
        if (strlen($keywords_add_2)) {
            $fulltext_needed_for[] = $TABLE_NAME . '.' . $FIELD_ADD_2;
        }
        conf('fulltext_needed_for', $fulltext_needed_for);

        // Try to pretty format SQL with missing lines
        $sql = str_replace(["\r", "\n\t\t\t\t\t\n", "\n\t\t\t\t\n", "\n\t\t\t\n"], "\n", $sql);
        $sql = str_replace(["\n\n\n", "\n\n"], "\n", $sql);
        // RETURN RESULT HERE
        if ($WHAT_TO_RETURN == 'sql') {
            return $sql;
        }
        if ($WHAT_TO_RETURN == 'fetch') {
            return db()->query_fetch_all($sql, $FIELD_ID);
        }
        if ($WHAT_TO_RETURN == 'stpl') {
            $data = db()->query_fetch_all($sql, $FIELD_ID);
            if ( ! $data) {
                return '';
            }
            // Get users infos
            $users_infos = null;
            if ($FIELD_USER && $FIELD_USER != -1) {
                $users_ids = [];
                foreach ((array) $data as $k => $v) {
                    if ($v[$FIELD_USER]) {
                        $users_ids[$v[$FIELD_USER]] = $v[$FIELD_USER];
                    }
                }
                if ( ! empty($users_ids)) {
                    $users_infos = user($users_ids);
                }
            }
            $data2 = null;
            foreach ((array) $data as $k => $v) {
                $data2[$k] = [
                    'id' => (int) ($v[$FIELD_ID]),
                    'date' => _format_date($v[$FIELD_DATE]),
                    'title' => _prepare_html($v[$FIELD_TITLE]),
                    'text' => _prepare_html(_substr($v[$FIELD_TEXT], 0, 200)),
                    'add_1' => _prepare_html(_substr($v[$FIELD_ADD_1], 0, 200)),
                    'add_2' => _prepare_html(_substr($v[$FIELD_ADD_2], 0, 200)),
                    'user_id' => (int) ($v[$FIELD_USER]),
                    'user_name' => _prepare_html(_display_name($users_infos[$v[$FIELD_USER]])),
                    'profile_link' => _profile_link($v[$FIELD_USER]),
                    'score' => _prepare_html($v[$FIELD_SCORE]),
                    'href' => process_url('./?object=' . ($_GET['object'] ?? null) . '&action=' . ($_GET['action'] ?? null) . '&id=' . (int) ($v[$FIELD_ID])),
                ];
            }
            $replace = [
                'data' => $data2,
                'source' => _prepare_html($SOURCE_ARRAY),
            ];
            return tpl()->parse($STPL_NAME, $replace);
        }
    }