Делаем "живой" поиск по заголовкам сайта
Пусть заголовки статей нашего сайта хранятся в некоторой таблице mySQLi, достаточно, чтобы для каждой статьи в записи базы имелось целочисленное поле id
с идентификатором записи, по которому потом можно сформировать ссылку для доступа к статье и строковое поле name
с заголовком записи.
Мы хотим, чтобы в текстовое поле можно было вводить произвольные фрагменты текста, а скрипт показывал кликабельный список статей, подходящих под запрос (см. скрин, фрагмент экрана):
живой поиск в работе, скриншот, фрагмент экрана
Для решения задачи нам понадобится немного PHP и немного Javascript, подгружать JQuery для уменьшения объёма трафика от скрипта не будем. Предполагается, что база и все файлы сохранены в кодировке Юникода UTF-8, а поиск должен работать для латиницы и кириллицы независимо от регистра. В коде использован тот же подход, что и при создании Sitemap.
Напишем файл index.php
, куда включим форму и яваскрипт. На самом деле, содержимое скрипта и формы может быть вставлено куда-то в нужный модуль сайта.
Файл index.php
<!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <title>Живой поиск по заголовкам</title> </head> <body> <script> function showResult (str) { str = str.replace(/[\&\+]/g,''); if (str.length==0) { document.getElementById("livesearch").innerHTML=''; document.getElementById("livesearch").style.border="0px"; return; } var xmlhttp=new XMLHttpRequest(); xmlhttp.onreadystatechange=function() { if (this.readyState==4 && this.status==200) { document.getElementById("livesearch").innerHTML=this.responseText; document.getElementById("livesearch").style.border="1px solid #ccc"; } } xmlhttp.open("GET","livesearch.php?q="+str,true); xmlhttp.send(); } </script> <form> <input type="text" size="30" onkeyup="showResult(this.value)"> <div id="livesearch"></div> </form> <p><a href="updatelinks.php">Переиндексировать поиск</a></p> </body> </html>
Легко увидеть, что этот файл вызывает модуль livesearch.php
, передавая ему наш запрос. Конечно же, этот модуль не будет каждый раз осуществлять реальный поиск по сайту, а подгрузит ранее сгенерированный файл links.xml
, содержащий нужную информацию.
Файл livesearch.php
<?php $xmlDoc=new DOMDocument(); $xmlDoc->load("./links.xml"); $x=$xmlDoc->getElementsByTagName('link'); $q=$_GET["q"]; if (mb_strlen($q.'UTF-8') > 0) { $hint=''; //подсказка for($i=0; $i<($x->length); $i++) { $y=$x->item($i)->getElementsByTagName('title'); $z=$x->item($i)->getElementsByTagName('url'); if ($y->item(0)->nodeType==1) { $url = $z->item(0)->childNodes->item(0)->nodeValue; $anchor = $y->item(0)->childNodes->item(0)->nodeValue; if (!empty($anchor) and !empty($q) and mb_stristr($anchor,$q,false,'UTF-8')) { //найти вхождения if (empty($hint)) { $hint = '<a href="'.$url.'" target="_blank">'.$anchor.'</a>'; } else { $hint .= '<br><a href="'.$url.'" target="_blank">'.$anchor.'</a>'; } } } } } if (empty($hint)) { $response = 'не найдено'; } else { $response = $hint; } echo $response; ?>
Сам файл links.xml
будет иметь простую структуру:
<pages> <link> <title>Заголовок страницы 1</title> <url>URL_страницы_1</url> </link> ... <link> <title>Заголовок страницы N</title> <url>URL_страницы_N</url> </link> </pages>
Он может генерироваться отдельным скриптом, который назовём updatelinks.php
. В этом скрипте должно
быть правильно прописано подключение к базе, основной URL Вашего сайта и, вероятнее всего, будет изменён блок кода "Формируем одну непустую ссылку". Тем не менее, исходник несложен для восприятия и поддаётся вашему переписыванию.
Файл updatelinks.php
<!DOCTYPE html> <html lang="ru"> <head> <meta charset="utf-8"> <title>Update links.xml</title> </head> <body> <?php error_reporting(E_ALL); //Контроль времени $time = 0; if (file_exists('./time.dat') === false) { file_put_contents ('./time.dat',time()) or die ("Не могу создать метку времени, проверьте права!"); } else { $time = file_get_contents ('./time.dat'); if ($time === false) die ("Не могу прочитать метку времени, проверьте правильность кода!"); } $delay = time() - intval($time); if ($delay <= 60*60) { //Можно переиндексировать не чаще раза в час echo '<p>Извините, переиндексировать базу можно через '.(floor((60*60 - $delay)/60)+1).' мин.</p>'."\n". '<p><a href="index.php">Вернуться к поиску</a></p></body></html>'; exit; } else { file_put_contents ('./time.dat',time()) or die ("Не могу создать метку времени, проверьте права!"); } //Данные для работы с MySQLi define ('DB_HOST','localhost'); //хост define ('DB_LOGIN','root'); //логин define ('DB_PASS',''); //пароль define ('DB_NAME','database'); //имя БД define ('DB_CODEPAGE','utf8'); //кодовая страница БД define ('TB_NAME','datatable'); //имя таблицы //Данные для генерации XML define ('FILE_NAME','links.xml'); //имя создаваемого файла .xml с данными define ('MAP_URL','http://blog.kislenko.net/'); //общий URL, с '/' В конце! define ('MAP_HEAD','<pages>'."\n"); //header файла .xml define ('MAP_FOOT','</pages>'); //footer файла .xml //Выполняемый код: $link=mysqli_connect(DB_HOST, DB_LOGIN, DB_PASS, DB_NAME); if (mysqli_connect_errno()) { printf ("<p>Не удалось подключиться: %s</p>\n", mysqli_connect_error()); exit (); } if (!mysqli_set_charset($link, DB_CODEPAGE)) { printf ("<p>Ошибка при загрузке набора символов: ".DB_CODEPAGE.": %s</p>\n", mysqli_error($link)); exit (); } $sql = 'select id, name from '.TB_NAME.' order by id asc'; //Формируем запрос SQL $result=mysqli_query($link,$sql); //Выполняем его $cnt = 0; //Будем считать пропущенные записи (без заголовка) $str = ''; if ($result and mysqli_num_rows($result)) { while ($row=mysqli_fetch_assoc ($result)) { $name = trim($row['name']); if (!empty($name)) { //Формируем одну непустую ссылку: $url = MAP_URL.'show.php?id='.$row['id']; $name = str_replace('&',' ',$name); //"Бродячий" амперсанд из заголовка убьёт XML! $str .= "<link>\n <title>$name</title>\n <url>$url</url>\n</link>\n"; } else { $cnt++; } } mysqli_free_result ($result); } else { $msg = mysqli_error ($link); echo "<p>Получено 0 строк по запросу: [$sql]</p>"; } $result_str = MAP_HEAD.$str.MAP_FOOT; $res = file_put_contents (FILE_NAME,$result_str); if ($res === false) { echo '<p>Ошибка записи файла '.FILE_NAME.', проверьте права</p>'."\n"; } else { echo '<p>Файл '.FILE_NAME.' успешно создан, пропущено записей без заголовка: '.$cnt.'</p>'."\n"; } mysqli_close ($link); echo '<p><a href="index.php">Вернуться к поиску</a></p>'; ?> </body> </html>
Так как мы не защищаем операцию "переиндексирования" сайта, разрешим выполнять её не чаще раза в час, а последнюю отметку времени сохраним в автосоздаваемом файле time.dat
.
Вот что вышло у меня, это действительно работает как "живой индекс" по моему блогу. Конечно, список может "вываливаться" с задержкой на секунду-другую, если объём файла links.xml
получился достаточно большим.
В том виде, что скрипт представлен сейчас, некоторые "критичные" для используемых технологий символы, такие как "&" и "+", участвовать в поиске не будут. Амперсанд мы просто убираем из данных XML, иначе не будет работать DOMDocument::load.
21.06.2021, 15:34 [1657 просмотров]