PHP поиск: методы и примеры кода для обработки запросов act search
Реализация поиска в PHP
Как организовать безопасный и производительный поиск по базе данных с учётом кириллицы?
Наиболее эффективное решение основано на использовании полнотекстового индекса MySQL (FULLTEXT) в связке с подготовленными запросами (prepared statements). Такой подход обеспечивает хорошую скорость поиска даже на больших объёмах данных, корректно обрабатывает кириллицу и исключает риск SQL-инъекций.
Шаги реализации:
- Создать таблицу с полнотекстовым индексом на нужных полях (например, title, content). Для кириллицы указать правильную collation, например utf8mb4_0900_ai_ci.
- В файле index.php обработать GET-параметр act=search, проверить его наличие и значение, отфильтровать пустую строку.
- Использовать PDO или mysqli с подготовленным запросом: SELECT * FROM articles WHERE MATCH(title, content) AGAINST(:query IN BOOLEAN MODE).
- Учесть минимальную длину слова (задаётся через ft_min_word_len) и список стоп-слов (stopwords). Для кириллицы может потребоваться настройка через innodb_ft_min_token_size.
// index.php?act=search&q=фреймворк PHP
$query = trim($_GET['q'] ?? '');
if (mb_strlen($query) < 2) {
// возвращать пустой результат или сообщение
}
$stmt = $pdo->prepare("SELECT id, title, content,
MATCH(title, content) AGAINST(:q IN BOOLEAN MODE) AS relevance
FROM articles
WHERE MATCH(title, content) AGAINST(:q IN BOOLEAN MODE)
ORDER BY relevance DESC");
$stmt->execute([':q' => '+' . implode(' +*', explode(' ', $query))]);
$results = $stmt->fetchAll();
Search tags php tag (поиск по тегам в php)
Типичные ошибки и их решения:
- Пустые результаты при корректном запросе - вероятно, слова короче ft_min_word_len (по умолчанию 4). Для кириллицы уменьшить до 2 через innodb_ft_min_token_size=2 в my.cnf.
- Игнорирование стоп-слов - список можно отключить параметром innodb_ft_enable_stopword=OFF или очистить таблицу stopwords.
- SQL-инъекция при формировании MATCH без prepared statement - всегда использовать параметризованные запросы.
- Неправильная collation - использовать utf8mb4_0900_ai_ci для корректного сравнения символов.
Цель варианта: организация поиска по веб-приложению с минимальной задержкой и встроенной поддержкой ранжирования релевантности. Подходит для сайтов с объёмом контента от тысяч до нескольких миллионов записей.
Как реализовать простой поиск с использованием оператора LIKE без полнотекстового индекса?
Когда нет доступа к созданию индексов или объём данных невелик (до 10-20 тысяч записей), допустим вариант с LIKE. Для кириллицы важно правильно обрабатывать регистр через COLLATE utf8mb4_general_ci.
$q = '%' . $query . '%';
$stmt = $pdo->prepare("SELECT * FROM articles WHERE title COLLATE utf8mb4_general_ci LIKE :q OR content COLLATE utf8mb4_general_ci LIKE :q");
$stmt->execute([':q' => $q]);
$results = $stmt->fetchAll();
Search topic php (поиск по теме в php)
Проблема: очень медленный поиск на больших таблицах (полный перебор). Решение - добавить ограничение по количеству записей (LIMIT 50) и комбинировать с ORDER BY. Также возможна фильтрация через REGEXP, но она ещё медленнее.
Цель: быстрая разработка без администрирования БД, для небольших проектов или прототипов.
Как искать похожие слова с учётом опечаток через Soundex или Levenshtein?
Для исправления опечаток в русскоязычных текстах можно применить расширение soundex_rus или встроенную функцию LEVENSHTEIN (доступна как пользовательская функция MySQL). Однако это ресурсоёмко и подходит только для небольших наборов (например, поиск по названиям товаров).
// пример с levenshtein в PHP (не в SQL)
function searchByLevenshtein($query, $titles) {
$results = [];
foreach ($titles as $title) {
$dist = levenshtein(mb_strtolower($query), mb_strtolower($title));
if ($dist <= 2) {
$results[] = ['title' => $title, 'distance' => $dist];
}
}
usort($results, fn($a, $b) => $a['distance'] <=> $b['distance']);
return $results;
}
Search type php id type (тип поиска по id в php)
Ошибка: не учитываются падежи и окончания. Решение - предварительно привести слова к нормальной форме (стемминг, например, phpMorphy).
Цель: исправление опечаток в узкой предметной области (например, каталог лекарств или фамилий).
Как подключить внешний поисковый движок (Elasticsearch / Sphinx) к index.php?
Для больших объёмов (миллионы записей) и сложных требований (морфология, автодополнение, фасеты) применяют специализированные решения. В index.php отправляется HTTP-запрос к поисковому серверу, а результат формируется из ответа.
// пример с Elasticsearch PHP client
$client = Elasticsearch\ClientBuilder::create()->build();
$params = [
'index' => 'articles',
'body' => [
'query' => [
'multi_match' => [
'query' => $query,
'fields' => ['title^3', 'content']
]
]
]
];
$response = $client->search($params);
$results = array_map(fn($hit) => $hit['_source'], $response['hits']['hits']);
Search php view (вид поиска в php)
Проблема: зависимость от внешнего сервиса, дополнительная инфраструктура. Решение - использовать Docker для развёртывания Elasticsearch в dev-среде, а в production обеспечить отказоустойчивость.
Цель: высокопроизводительный поиск с продвинутой аналитикой. Подходит для интернет-магазинов, новостных порталов.
Как добавить пагинацию и постраничный вывод результатов поиска?
После выполнения запроса результаты разбиваются на страницы. Параметры page и per_page передаются через GET. Важно сохранить original query для подстановки в ссылки.
$perPage = 10;
$page = max(1, (int)($_GET['page'] ?? 1));
$offset = ($page - 1) * $perPage;
$stmt = $pdo->prepare("SELECT SQL_CALC_FOUND_ROWS * FROM articles
WHERE MATCH(title, content) AGAINST(:q IN BOOLEAN MODE)
LIMIT :limit OFFSET :offset");
$stmt->bindValue(':q', $query, PDO::PARAM_STR);
$stmt->bindValue(':limit', $perPage, PDO::PARAM_INT);
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
$stmt->execute();
$results = $stmt->fetchAll();
$total = $pdo->query("SELECT FOUND_ROWS()")->fetchColumn();
$pages = ceil($total / $perPage);
Ошибка: потеря параметра q при переходе по страницам. Решение - передавать все параметры в ссылках через http_build_query.
Цель: удобная навигация по большому числу результатов.
Расширенные примеры реализации поиска в PHP
Многоуровневое ранжирование с FULLTEXT BOOLEAN MODE
Пример демонстрирует использование булевого режима для точной настройки веса слов: слова с префиксом '+' обязательны, с '-' исключаются, '*' - подстановка. Результат сортируется по релевантности.
// index.php?act=search&q=+PHP -JavaScript *разработка
$rawQuery = $_GET['q'] ?? '';
$terms = preg_split('/\s+/', $rawQuery);
$booleanQuery = '';
foreach ($terms as $term) {
if ($term[0] === '+') {
$booleanQuery .= '+' . substr($term, 1) . ' ';
} elseif ($term[0] === '-') {
$booleanQuery .= '-' . substr($term, 1) . ' ';
} else {
$booleanQuery .= $term . '* ';
}
}
$stmt = $pdo->prepare("SELECT
title,
MATCH(title, content) AGAINST(:q IN BOOLEAN MODE) AS relevance
FROM articles
WHERE MATCH(title, content) AGAINST(:q IN BOOLEAN MODE)
HAVING relevance > 0
ORDER BY relevance DESC
LIMIT 20");
$stmt->execute([':q' => $booleanQuery]);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
Результат (массив):
[
['title' => 'PHP для начинающих', 'relevance' => 8.53],
['title' => 'Сравнение PHP и Node.js', 'relevance' => 5.21],
...
]
Поиск с автодополнением (suggest) через Elasticsearch Completion Suggester
Позволяет выводить подсказки по мере ввода запроса. В PHP отправляется запрос к Elasticsearch с типом suggest.
$client = Elasticsearch\ClientBuilder::create()->build();
$params = [
'index' => 'articles',
'body' => [
'suggest' => [
'title-suggest' => [
'prefix' => $query,
'completion' => [
'field' => 'title_suggest',
'fuzzy' => ['fuzziness' => 'auto']
]
]
]
]
];
$response = $client->search($params);
$suggestions = array_map(
fn($opt) => $opt['text'],
$response['suggest']['title-suggest'][0]['options']??[]
);
Результат:
[
"PHP фреймворки",
"PHPstorm настройка",
"PHPUnit тестирование"
]
Поиск только среди заголовков с учётом стемминга (phpMorphy)
Для русского языка стемминг повышает точность. Библиотека phpMorphy приводит слова к нормальной форме. Затем поиск выполняется по этим формам.
require_once '/path/to/phpMorphy/src/common.php';
$morphy = new phpMorphy\Morphy('ru');
$words = explode(' ', $query);
$normalized = [];
foreach ($words as $word) {
$base = $morphy->getBaseForm(mb_strtolower($word));
$normalized[] = $base ?: $word;
}
$queryNormal = implode(' ', $normalized);
// далее полнотекстовый поиск по нормализованной строке
Замечание: стемминг может быть избыточным для коротких запросов. Лучше применять только при длине более 3 символов.
Поиск с подсветкой найденных фрагментов (snippet)
Выделение совпадений в результатах улучшает UX. Используется функция PHP str_ireplace или регулярное выражение с границами слов.
function highlight($text, $query) {
$words = explode(' ', $query);
$pattern = '/(' . implode('|', array_map('preg_quote', $words)) . ')/ui';
return preg_replace($pattern, '$0', htmlspecialchars($text));
}
foreach ($results as &$row) {
$row['content'] = highlight($row['content'], $query);
}
Исходный текст: "PHP - скриптовый язык программирования" Результат: "PHP - скриптовый язык программирования"
Поиск по связанным тегам (многие ко многим) с помощью JOIN
Если есть таблицы articles, tags, article_tags, поиск может включать теги.
$stmt = $pdo->prepare("SELECT DISTINCT a.id, a.title
FROM articles a
JOIN article_tags at ON a.id = at.article_id
JOIN tags t ON at.tag_id = t.id
WHERE t.name LIKE :tag_query
OR a.title LIKE :title_query");
$q = '%' . $query . '%';
$stmt->execute([':tag_query' => $q, ':title_query' => $q]);
$results = $stmt->fetchAll();