Скрипт форума на PHP своими руками. Часть 7
Какой же форум без поиска? Вот о нем и поговорим сегодня. Функция 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().': '.mysql_error().'<br/>'.
'(Файл '. __FILE__ .', строка '. __LINE__ .')';
return showErrorMessage( $msg, $err, true, '' );
}
if ( mysql_num_rows( $res ) > 0 ) {
$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> </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'], 0, 64 );
// Убираем пробелы в начале и конце строки поиска
$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) < 3 ) 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 < 0 ) $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;
}
?>
Для извлечения корней русских слов используется стеммер Портера.
Дальше - три вспомогательные функции. Функция 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( $res, 0, 0 );
if ( $total == 0 ) return 'По вашему запросу ничего не найдено';
// Число страниц результатов поиска (постраничная навигация)
$cntPages = ceil( $total / SEARCH_THEMES_PER_PAGE );
// Проверяем передан ли номер текущей страницы (постраничная навигация)
if ( isset($_GET['page']) ) {
$page = (int)$_GET['page'];
if ( $page < 1 ) $page = 1;
} else {
$page = 1;
}
if ( $page > $cntPages ) $page = $cntPages;
// Начальная позиция (постраничная навигация)
$start = ( $page - 1 ) * SEARCH_THEMES_PER_PAGE;
// Строим постраничную навигацию, если это необходимо
if ( $cntPages > 1 ) {
// Функция возвращает 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( $res, 0, 0 );
if ( $total == 0 ) return 'По вашему запросу ничего не найдено';
// Число страниц результатов поиска (постраничная навигация)
$cntPages = ceil( $total / SEARCH_POSTS_PER_PAGE );
// Проверяем передан ли номер текущей страницы (постраничная навигация)
if ( isset($_GET['page']) ) {
$page = (int)$_GET['page'];
if ( $page < 1 ) $page = 1;
} else {
$page = 1;
}
if ( $page > $cntPages ) $page = $cntPages;
// Начальная позиция (постраничная навигация)
$start = ( $page - 1 ) * SEARCH_POSTS_PER_PAGE;
// Строим постраничную навигацию, если это необходимо
if ( $cntPages > 1 ) {
// Функция возвращает 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( $res, 0, 0 );
if ( $total == 0 ) return 'По вашему запросу ничего не найдено';
// Число страниц результатов поиска (постраничная навигация)
$cntPages = ceil( $total / SEARCH_EVERYWHERE_PER_PAGE );
// Проверяем передан ли номер текущей страницы (постраничная навигация)
if ( isset($_GET['page']) ) {
$page = (int)$_GET['page'];
if ( $page < 1 ) $page = 1;
} else {
$page = 1;
}
if ( $page > $cntPages ) $page = $cntPages;
// Начальная позиция (постраничная навигация)
$start = ( $page - 1 ) * SEARCH_EVERYWHERE_PER_PAGE;
// Строим постраничную навигацию, если это необходимо
if ( $cntPages > 1 ) {
// Функция возвращает 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;
}
?>
Продолжение следует...
|