Скрипт форума на PHP своими руками. Часть 7


Прислал: Евгений Токмаков [ 03.07.2008 @ 12:57 ]
Раздел:: [ Статьи по PHP ]


Какой же форум без поиска? Вот о нем и поговорим сегодня. Функция searchForm() возвращает html формы для поиска по форуму.

<?php
// Функция возвращает html формы для поиска по форуму
function searchForm()
{
  
$html '';
  
  
$query "SELECT id_forum, name FROM ".TABLE_FORUMS." WHERE 1 ORDER BY pos";
  
$res mysql_query$query );
  if ( !
$res ) {
    
$msg 'Ошибка при формировании формы для поиска';
    
$err 'Ошибка при выполнении запроса: <br/>'.
           
$query.'<br/>'.mysql_errno().':&nbsp;'.mysql_error().'<br/>'.
           
'(Файл '__FILE__ .', строка '__LINE__ .')';
    return 
showErrorMessage$msg$errtrue'' );
  }
  if ( 
mysql_num_rows$res ) > ) {
    
$options '<option value="0">Все имеющиеся</option>'."\n";
    while( 
$forum mysql_fetch_row$res ) ) {
      
$options $options.'<option value="'.$forum[0].'">'.$forum[1].'</option>'."\n";
    }
    
$html file_get_contents'./templates/searchForm.html' );
    
$action $_SERVER['PHP_SELF'].'?action=searchResult';
    
$html str_replace'{options}'$options$html );
    
$html str_replace'{action}'$action$html );
  }
  
  return 
$html;
}
?>

Файл шаблона поиска по форуму:

<form action="{action}" method="POST" name="searchForm">

<table class="showTable">
<tr>
  <th colspan="2">Поиск по форуму</th>
</tr>
<tr>
   <td width="30%" align="right">Что искать:</td>
   <td width="70%"><input type="text" name="words" maxlength="64" value="" style="width:300px" /></td>
</tr>
<tr>
   <td align="right">Форум:</td>
   <td>
   <select name="id_forum" style="width:300px">
   {options}
   </select>
   </td>
</tr>
<tr>
   <td align="right">Где искать:</td>
   <td>
   <input type="radio" name="where" value="themes" checked /> Искать только в названиях тем<br/>
   <input type="radio" name="where" value="posts" /> Искать только в текстах сообщений<br/>
   <input type="radio" name="where" value="everywhere" /> Искать в названиях тем и текстах сообщений
   </td>
</tr>
<tr>
  <td>&nbsp;</td>
  <td><input type="submit" name="sendForm" value="Искать" /></td>
</tr>
</table>

</form>

Функция searchResult() возвращает результат поиска по форуму:

<?php
// Функция возвращает результат поиска по форуму
function searchResult()
{

  if ( isset( 
$_POST['words'] ) and
       isset( 
$_POST['id_forum'] ) and
       isset( 
$_POST['where'] ) )
  {    
    if ( empty( 
$_POST['words'] ) ) {
      
header'Location: '.$_SERVER['PHP_SELF'].'?action=searchForm' );
      die();  
    }
    
// Обрезаем строку до длины, указанной в атрибуте maxlength
    
$search substr$_POST['words'], 064 );
    
// Убираем пробелы в начале и конце строки поиска
    
$search trim$search );
    
// Убираем все "ненормальные" символы
    
$good preg_replace("#[^-a-zа-я\s]#i"" "$search);
    
$good trim$good );
    if ( empty( 
$good ) ) {
      
header'Location: '.$_SERVER['PHP_SELF'].'?action=searchForm' );
      die();  
    }
    
// Сжимаем двойные пробелы
    
$good ereg_replace(" +"" "$good);
        
    
// Получаем корни искомых слов
    
$stemmer = new Lingua_Stem_Ru();
    
$tmp explode" "$good );
    foreach ( 
$tmp as $wrd ) {
      
// Если слово слишком короткое - не используем его
      
if ( strlen($wrd) < ) continue;
      
$words[] = $stemmer->stem_word($wrd);
    }
    
// Склеиваем массив $words обратно в строку
    
$string implode"* "$words );
    
$string $string."*";
  
    
// Теперь надо выяснить, где будем искать
    
$where $_POST['where'];
    
$whereArray = array( 'themes''posts''everywhere' );
    if ( !
in_array$where$whereArray ) ) $where 'themes';
    
    
// Записываем все данные в сессию - это нам понадобится при 
    // построении постраничной навигации результатов поиска
    
$_SESSION['search']['query'] = $search;
    
$_SESSION['search']['good'] = $good;
    
$_SESSION['search']['words'] = $words;
    
// Это нам потребуется для подсветки искомых слов
    
$_SESSION['search']['words'] = implode'|'$words );
    
$_SESSION['search']['string'] = $string;
    
$_SESSION['search']['where'] = $where;
    
$id_forum = (int)$_POST['id_forum'];
    if ( 
$id_forum $id_forum 0;
    
$_SESSION['search']['id_forum'] = $id_forum;
    
    
header'Location: '.$_SERVER['PHP_SELF'].'?action=searchResult' );
    die();
  }
  
  if ( !isset( 
$_SESSION['search'] ) ) {
    
header'Location: '.$_SERVER['PHP_SELF'] );
    die();  
  }
    
  
  if ( 
$_SESSION['search']['where'] == 'themes' )
    
$result searchResultThemes(); // Если поиск только по названиям тем
  
else if ( $_SESSION['search']['where'] == 'posts' )
    
$result searchResultPosts(); // Если поиск только в сообщениях (постах)
  
else
    
$result searchResultEverywhere(); // Если поиск в названиях тем и сообщениях
    
  
$html '<table class="showTable">'."\n";
  
$html $html.'<tr>'."\n";
  
$html $html.'<th>Результаты поиска</th>'."\n";
  
$html $html.'<tr>'."\n";
  
$html $html.'</table>'."\n";

  
$html $html.$result."\n";

  return 
$html;
}
?>

Для извлечения корней русских слов используется стеммер Портера ( http://webscript.ru///forum.dklab.ru/php/advises/HeuristicWithoutTheDictionaryExtractionOfARootFromRussianWord.html ).

Дальше - три вспомогательные функции. Функция searchResultThemes() возвращает результат поиска только в названиях тем:

<?php
// Поиск только в названиях тем
function searchResultThemes()
{
  
// Составляем запрос к БД, чтобы узнать количество записей в результатах поиска - 
  // это нужно для построения постраничной навигации
  
$forum '';
  if ( 
$_SESSION['search']['id_forum'] ) $forum ' AND id_forum='.$_SESSION['search']['id_forum'];
  
$query "SELECT COUNT(*)
            FROM "
.TABLE_THEMES.
            WHERE MATCH (name) AGAINST ('"
.$_SESSION['search']['string']."' IN BOOLEAN MODE)".$forum;           
  
$res mysql_query$query );
  
$total mysql_result$res0);
  if ( 
$total == ) return 'По вашему запросу ничего не найдено';
 
  
// Число страниц результатов поиска (постраничная навигация)
  
$cntPages ceil$total SEARCH_THEMES_PER_PAGE );
  
  
// Проверяем передан ли номер текущей страницы (постраничная навигация)
  
if ( isset($_GET['page']) ) {
    
$page = (int)$_GET['page'];
    if ( 
$page $page 1;
  } else {
    
$page 1;
  }

  if ( 
$page $cntPages $page $cntPages;
  
// Начальная позиция (постраничная навигация)
  
$start = ( $page ) * SEARCH_THEMES_PER_PAGE;

  
// Строим постраничную навигацию, если это необходимо
  
if ( $cntPages ) {
    
// Функция возвращает html меню для постраничной навигации
    
$pages pageIterator$page$cntPages$_SERVER['PHP_SELF'].'?action=searchResult' );           
  }

  
$query "SELECT id_theme, name, id_forum 
            FROM "
.TABLE_THEMES.
            WHERE MATCH (name) AGAINST ('"
.$_SESSION['search']['string']."' IN BOOLEAN MODE)".$forum."
            ORDER BY MATCH (name) AGAINST ('"
.$_SESSION['search']['string']."' IN BOOLEAN MODE) DESC 
            LIMIT "
.$start.", ".SEARCH_THEMES_PER_PAGE;            
  
$res mysql_query$query );
  
$html "<ul>\n";
  while ( 
$theme mysql_fetch_array$res ) ) {
    
$html $html.'<li><a class="topictitle" href="'.$_SERVER["PHP_SELF"]."?action=showTheme".
            
'&idForum='.$theme['id_forum'].'&id_theme='.$theme['id_theme'].'&page=1">'.
            
$theme['name'].'</a></li>'."\n";    
  }
  
$html $html."</ul>\n";

  
// Постраничная навигация
  
if ( isset( $pages ) ) $html $html.$pages."\n";
  
  return 
$html;    
}
?>

Функция searchResultPosts() возвращает результат поиска только в сообщениях (постах):

<?php
// Поиск только в сообщениях (постах)
function searchResultPosts()
{
  
// Составляем запрос к БД, чтобы узнать количество записей в результатах поиска - 
  // это нужно для построения постраничной навигации
  
$forum '';
  if ( 
$_SESSION['search']['id_forum'] ) $forum ' AND b.id_forum='.$_SESSION['search']['id_forum'];
  
$query "SELECT COUNT(*) 
            FROM "
.TABLE_POSTS." a INNER JOIN ".TABLE_THEMES." b
            ON a.id_theme=b.id_theme 
            WHERE MATCH (a.name) AGAINST ('"
.$_SESSION['search']['string']."' IN BOOLEAN MODE)".$forum;
            
  
$res mysql_query$query );
  
$total mysql_result$res0);

  if ( 
$total == ) return 'По вашему запросу ничего не найдено';
 
  
// Число страниц результатов поиска (постраничная навигация)
  
$cntPages ceil$total SEARCH_POSTS_PER_PAGE );
  
  
// Проверяем передан ли номер текущей страницы (постраничная навигация)
  
if ( isset($_GET['page']) ) {
    
$page = (int)$_GET['page'];
    if ( 
$page $page 1;
  } else {
    
$page 1;
  }

  if ( 
$page $cntPages $page $cntPages;
  
// Начальная позиция (постраничная навигация)
  
$start = ( $page ) * SEARCH_POSTS_PER_PAGE;

  
// Строим постраничную навигацию, если это необходимо
  
if ( $cntPages ) {
    
// Функция возвращает html меню для постраничной навигации
    
$pages pageIterator$page$cntPages$_SERVER['PHP_SELF'].'?action=searchResult' );           
  }

  
$query "SELECT a.id_post, a.name, a.id_theme, b.id_forum, b.name 
            FROM "
.TABLE_POSTS." a INNER JOIN ".TABLE_THEMES." b
            ON a.id_theme=b.id_theme 
            WHERE MATCH (a.name) AGAINST ('"
.$_SESSION['search']['string']."' IN BOOLEAN MODE)".$forum."
            ORDER BY MATCH (a.name) AGAINST ('"
.$_SESSION['search']['string']."' IN BOOLEAN MODE) DESC
            LIMIT "
.$start.", ".SEARCH_POSTS_PER_PAGE;
        
  
$res mysql_query$query );
  
$html '';
  while ( 
$post mysql_fetch_row$res ) ) {
    
    
$html $html.'<div style="margin: 5px 0 5px 0">'."\n";
    
$html $html.'<img src="./images/folder.gif" width="19" height="18" alt="" align="top" />
            <a class="topictitle" href="'
.$_SERVER["PHP_SELF"]."?action=showTheme".
            
'&idForum='.$post[3].'&id_theme='.$post[2].'&page=1">'.$post[4].'</a>'."\n";
    
$html $html.'</div>'."\n";        
    
    
$html $html.'<table class="postTable">'."\n";
    
$html $html.'<tr>'."\n";
    
$html $html.'<td>'."\n";
    
$message print_page$post[1] );
    
$message preg_replace("/\b(".$_SESSION['search']['words'].")(.*?)\b/i"
               
"<span style='color:red; font-weight:bold'>\\0</span>"$message);
    
$html $html.$message."\n";
    
$html $html.'</td>'."\n";
    
$html $html.'</tr>'."\n";
    
$html $html."</table>\n";
  }

  
// Постраничная навигация
  
if ( isset( $pages ) ) $html $html.$pages."\n";
  
  return 
$html;    
}
?>

Функция searchResultEverywhere() возвращает результат поиска в названиях тем и сообщениях (постах):

<?php
// Поиск в названиях тем и сообщениях
function searchResultEverywhere()
{
  
// Составляем запрос к БД, чтобы узнать количество записей в результатах поиска - 
  // это нужно для построения постраничной навигации
  
$forum '';
  if ( 
$_SESSION['search']['id_forum'] ) $forum ' AND b.id_forum='.$_SESSION['search']['id_forum'];
  
$query "SELECT COUNT(*) 
            FROM "
.TABLE_POSTS." a INNER JOIN ".TABLE_THEMES." b
            ON a.id_theme=b.id_theme 
            WHERE MATCH (a.name, b.name) AGAINST ('"
.$_SESSION['search']['string']."' IN BOOLEAN MODE)".$forum;
            
  
$res mysql_query$query );
  
$total mysql_result$res0);

  if ( 
$total == ) return 'По вашему запросу ничего не найдено';
 
  
// Число страниц результатов поиска (постраничная навигация)
  
$cntPages ceil$total SEARCH_EVERYWHERE_PER_PAGE );
  
  
// Проверяем передан ли номер текущей страницы (постраничная навигация)
  
if ( isset($_GET['page']) ) {
    
$page = (int)$_GET['page'];
    if ( 
$page $page 1;
  } else {
    
$page 1;
  }

  if ( 
$page $cntPages $page $cntPages;
  
// Начальная позиция (постраничная навигация)
  
$start = ( $page ) * SEARCH_EVERYWHERE_PER_PAGE;

  
// Строим постраничную навигацию, если это необходимо
  
if ( $cntPages ) {
    
// Функция возвращает html меню для постраничной навигации
    
$pages pageIterator$page$cntPages$_SERVER['PHP_SELF'].'?action=searchResult' );           
  }

  
$query "SELECT a.id_post, a.name, a.id_theme, b.id_forum, b.name 
            FROM "
.TABLE_POSTS." a INNER JOIN ".TABLE_THEMES." b
            ON a.id_theme=b.id_theme 
            WHERE MATCH (a.name, b.name) AGAINST ('"
.$_SESSION['search']['string']."' IN BOOLEAN MODE)".$forum."
            ORDER BY MATCH (a.name, b.name) AGAINST ('"
.$_SESSION['search']['string']."' IN BOOLEAN MODE) DESC
            LIMIT "
.$start.", ".SEARCH_EVERYWHERE_PER_PAGE;
        
  
$res mysql_query$query );
  
$html ''
  while ( 
$post mysql_fetch_row$res ) ) {
  
    
$html $html.'<div style="margin: 5px 0 5px 0">'."\n";
    
$html $html.'<img src="./images/folder.gif" width="19" height="18" alt="" align="top" />
            <a class="topictitle" href="'
.$_SERVER["PHP_SELF"]."?action=showTheme".
            
'&idForum='.$post[3].'&id_theme='.$post[2].'&page=1">'.$post[4].'</a>'."\n";
    
$html $html.'</div>'."\n";        
    
    
$html $html.'<table class="postTable">'."\n";    
    
$html $html.'<tr>'."\n";
    
$html $html.'<td>'."\n";
    
$message print_page$post[1] );
    
$message preg_replace("/\b(".$_SESSION['search']['words'].")(.*?)\b/i"
               
"<span style='color:red; font-weight:bold'>\\0</span>"$message);
    
$html $html.$message."\n";
    
$html $html.'</td>'."\n";
    
$html $html.'</tr>'."\n";
    
$html $html."</table>\n";
  }
  
  
// Постраничная навигация
  
if ( isset( $pages ) ) $html $html.$pages."\n";
  
  return 
$html;
}
?>

Продолжение следует...