Реализация поиска на сайте с помощью PHP

Раздел: Веб-разработка -> Реализация поиска

Форма поиска – один из ключевых элементов любого информационного сайта. В этой статье рассматриваются разные подходы к её реализации на PHP: от простого LIKE до полнотекстового индекса и асинхронных запросов. Каждый вариант сопровождается кодом, пояснениями и типичными ошибками.

Основные подходы к реализации формы поиска

Как реализовать безопасный и быстрый поиск по базе данных?

Наиболее эффективное решение - использование PDO с подготовленными запросами и встроенного полнотекстового поиска MySQL (FULLTEXT). Это обеспечивает защиту от SQL-инъекций и высокую производительность.


<!-- searchform.html -->
<form method="GET" action="search.php">
  <input type="text" name="q" placeholder="Поиск...">
  <button type="submit">Найти</button>
</form>
  

Search tags php tag (поиск по тегам в php)


<?php
// search.php
$query = $_GET['q'] ?? '';
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', 'user', 'pass');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$sql = 'SELECT id, title, content,
               MATCH(title, content) AGAINST(:query IN BOOLEAN MODE) AS relevance
        FROM articles
        WHERE MATCH(title, content) AGAINST(:query2 IN BOOLEAN MODE)
        ORDER BY relevance DESC';
$stmt = $pdo->prepare($sql);
$stmt->execute(['query' => $query, 'query2' => $query]);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
?>
  

Search topic php (поиск по теме в php)

Пояснение шагов:

  1. Форма отправляет запрос методом GET - это позволяет сохранить поисковый запрос в URL для закладок.
  2. PDO-подключение с кодировкой utf8mb4 для поддержки эмодзи.
  3. Полнотекстовый поиск в булевом режиме: поддерживает операторы + (обязательное слово), - (исключение), * (маска).
  4. Релевантность (0-1) вычисляется на лету и используется для сортировки.

Типичные ошибки и их решение

  • Ошибка 1064 «FULLTEXT index not found» - убедитесь, что для столбцов title и content создан полнотекстовый индекс: CREATE FULLTEXT INDEX ft_articles ON articles(title, content);.
  • Пустой запрос возвращает все строки? Добавьте проверку: if (empty(trim($query))) { echo 'Введите запрос'; exit; }.
  • Не экранируются специальные символы в булевом режиме (например, +, -, *, (), ~). Следует очищать запрос от недопустимых операторов или экранировать их.
  • Для InnoDB полнотекстовый поиск требует минимальной длины слова (по умолчанию 3). Короткие запросы (например, «PHP») проходят, а «JS» - нет. Настройте innodb_ft_min_token_size.

1. Простой поиск с LIKE

Как выполнить поиск по части слова с использованием LIKE?


$stmt = $pdo->prepare('SELECT * FROM articles WHERE title LIKE :query');
$stmt->execute(['query' => '%' . $query . '%']);
  

Search type php id type (тип поиска по id в php)

Цель: быстрый прототип для небольших объёмов данных.

Проблемы:

  • Неэффективен при тысячах записей (сканирование таблицы).
  • Не учитывает регистр для кириллицы (настройка COLLATION).
  • SQL-инъекции предотвращаются подготовленными запросами, но LIKE сам по себе не защищает.

2. Поиск через POST

Когда следует использовать POST для поискового запроса?


<form method="POST" action="search.php">
  <input type="text" name="q">
  <button type="submit">Поиск</button>
</form>
  

Search php view (вид поиска в php)

Обработчик получает данные через $_POST. Такой подход подходит для поиска с чувствительными данными (например, внутренний поиск по личным документам).

Недостаток: результат нельзя передать другому пользователю через ссылку (параметры не в URL). Для большинства сайтов предпочтителен GET.

3. Поиск с фильтрацией по категориям и датам

Как добавить дополнительные параметры фильтрации?


<form method="GET">
  <input name="q" placeholder="Текст">
  <select name="category">
    <option value="">Все категории</option>
    <option value="1">Новости</option>
    <option value="2">Статьи</option>
  </select>
  <input type="date" name="date_from">
  <button>Искать</button>
</form>
  

Catalog php search (поиск в каталоге php)


$where = [];
$params = [];

if (!empty($_GET['q'])) {
    $where[] = 'MATCH(title,content) AGAINST(:q IN BOOLEAN MODE)';
    $params['q'] = $_GET['q'];
}
if (!empty($_GET['category'])) {
    $where[] = 'category_id = :cat';
    $params['cat'] = (int)$_GET['category'];
}
if (!empty($_GET['date_from'])) {
    $where[] = 'created_at >= :date';
    $params['date'] = $_GET['date_from'];
}

$sql = 'SELECT * FROM articles';
if ($where) $sql .= ' WHERE ' . implode(' AND ', $where);
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
  

Index php act search (действие поиска в php)

Сложности: динамическое построение WHERE требует тщательного экранирования. В приведённом коде значения category и date приводятся к целому числу или строке, что безопасно. Для текстовых полей используйте подготовленные запросы.

4. AJAX-поиск без перезагрузки страницы

Как реализовать мгновенный поиск без перезагрузки?


// frontend (jQuery)
$('#search-form').on('submit', function(e) {
    e.preventDefault();
    $.get('search_ajax.php', {q: $('#query').val()}, function(data) {
        $('#results').html(data);
    });
});
  

Search php cid (поиск по cid в php)


<?php
// search_ajax.php
$query = $_GET['q'] ?? '';
// ... запрос с PDO
$stmt = $pdo->prepare('SELECT title, content FROM articles WHERE MATCH...');
$stmt->execute(['q' => $query]);
$rows = $stmt->fetchAll();
echo '<ul>';
foreach ($rows as $row) {
    echo '<li>' . htmlspecialchars($row['title']) . '</li>';
}
echo '</ul>';
?>
  

Search php keyword (поиск по ключевому слову в php)

Типичные проблемы:

  • Не учитывается кодировка - используйте header('Content-Type: text/html; charset=utf-8');.
  • Большой объём данных - лучше возвращать JSON и строить DOM на клиенте.
  • Частые запросы - ввести debounce (задержку) и отмену предыдущего запроса.

5. Пагинация результатов поиска

Как организовать постраничный вывод результатов?


$page = max(1, (int)($_GET['page'] ?? 1));
$perPage = 10;
$offset = ($page - 1) * $perPage;

$totalStmt = $pdo->prepare('SELECT COUNT(*) FROM articles WHERE MATCH(title,content) AGAINST(:q IN BOOLEAN MODE)');
$totalStmt->execute(['q' => $query]);
$total = $totalStmt->fetchColumn();
$totalPages = ceil($total / $perPage);

$stmt = $pdo->prepare('SELECT * 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();
  

Search php search name (поиск по имени в php)

Ошибки:

  • Забыть отсортировать - результаты будут перемешаны. Добавьте ORDER BY.
  • Большие смещения (OFFSET) замедляют запрос. Для больших таблиц альтернатива - курсорная пагинация.
  • Параметры LIMIT и OFFSET должны передаваться как целые числа, иначе ошибка PDO.

6. Поиск по нескольким полям с OR

Как искать одновременно в заголовке и содержимом?


$stmt = $pdo->prepare('SELECT * FROM articles WHERE title LIKE :q1 OR content LIKE :q2');
$stmt->execute(['q1' => '%'.$query.'%', 'q2' => '%'.$query.'%']);
  

Search index php subaction (поддействие поиска в php)

Полнотекстовый индекс эффективнее, но если его нет, OR с LIKE - единственный вариант.

Проблема: при OR индексы (если они есть) могут не использоваться - сканирование двух столбцов. Решение: создать составной индекс или перейти на MATCH AGAINST.

7. Сортировка по релевантности с MATCH AGAINST

Как получить результаты, отсортированные по релевантности?


SELECT id, title,
       MATCH(title,content) AGAINST('+PHP -MySQL' IN BOOLEAN MODE) AS relevance
FROM articles
HAVING relevance > 0
ORDER BY relevance DESC;
  

Значение релевантности можно вывести пользователю или использовать только для сортировки. HAVING отсекает нулевую релевантность.

Сложности: релевантность нормализуется по-разному в зависимости от длины документа. Для стабильных результатов используйте булевый режим с весами (IN BOOLEAN MODE не даёт нормализованной релевантности, но сортировка всё равно работает).

- Index php page search (страница поиска в php)
- Index php search id (поиск по идентификатору в php)
- Search products php (php скрипт для поиска товаров)

Расширенные примеры

1. Полнотекстовый поиск в булевом режиме с расширенными операторами

Булевый режим поддерживает гибкие шаблоны. Пример запроса с оператором подстановки * и группировкой:

Пример

$query = '+разработк* + (PHP | Python) -учебник';
$sql = "SELECT *, MATCH(title,content) AGAINST(:q IN BOOLEAN MODE) AS rel
        FROM articles
        WHERE MATCH(title,content) AGAINST(:q2 IN BOOLEAN MODE)
        ORDER BY rel DESC";
$stmt = $pdo->prepare($sql);
$stmt->execute(['q' => $query, 'q2' => $query]);
$data = $stmt->fetchAll();
Массив результатов с полем rel (нечисловой, но порядок сортировки верный).

Пояснение: +разработк* - обязательное слово, оканчивающееся на любые символы. (PHP | Python) - хотя бы одно из слов. -учебник - исключает записи. Внимание: операторы чувствительны к экранированию, если они часть запроса.

2. Регулярные выражения (REGEXP) для сложных шаблонов

Если нужно найти слова, начинающиеся с определённой буквы или содержащие цифры:

Пример

$pattern = '^[А-Я].*[0-9]';
$stmt = $pdo->prepare('SELECT * FROM articles WHERE title REGEXP :pat');
$stmt->execute(['pat' => $pattern]);
$res = $stmt->fetchAll();
Выборка заголовков, начинающихся с заглавной кириллицы и содержащих цифру.

Важно: REGEXP не использует индексы, поэтому применим только для малых таблиц или разовых операций.

3. Интеграция с Elasticsearch через официальный клиент (elasticsearch-php)

Для высокопроизводительного поиска на десятках тысяч записей:

Пример

composer require elasticsearch/elasticsearch
Пример

<?php
require 'vendor/autoload.php';
use Elastic\Elasticsearch\ClientBuilder;

$client = ClientBuilder::create()->setHosts(['localhost:9200'])->build();

// Индексация
$params = [
    'index' => 'articles',
    'id' => 1,
    'body' => ['title' => 'PHP Search Form', 'content' => '...']
];
$client->index($params);

// Поиск
$searchParams = [
    'index' => 'articles',
    'body' => [
        'query' => ['match' => ['title' => 'PHP']]
    ]
];
$response = $client->search($searchParams);
$hits = $response['hits']['hits'];
JSON-ответ со списком найденных документов и score.

Пояснение: Elasticsearch обеспечивает молниеносный полнотекстовый поиск с поддержкой морфологии, автодополнения и фасетов. Требуется отдельный сервер.

4. Кэширование поисковых запросов через Redis

Чтобы повторные запросы не нагружали базу, кэшируем результаты:

Пример

$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

$cacheKey = 'search_' . md5($query);
if ($redis->exists($cacheKey)) {
    $results = unserialize($redis->get($cacheKey));
} else {
    // выполняем запрос к БД
    $stmt = $pdo->prepare('...');
    $stmt->execute(['...']);
    $results = $stmt->fetchAll();
    $redis->setex($cacheKey, 300, serialize($results)); // на 5 минут
}
При первом запросе данные из БД, при повторном - из Redis (быстрее).

Нюансы: нужно чистить кэш после добавления/удаления статей. Хеш запроса должен учитывать все параметры (страница, категория).

5. Поиск с учётом морфологии через phpMorphy

Библиотека приводит слова к нормальной форме (например, «бегали» -> «бегать»). Пример:

Пример

require_once 'phpMorphy/src/common.php';
$morphy = new phpMorphy\Morphy('ru');

$words = explode(' ', mb_strtolower($query));
$normalized = [];
foreach ($words as $word) {
    $lemmas = $morphy->getBaseForm($word);
    $normalized[] = $lemmas ? $lemmas[0] : $word;
}
$normalQuery = implode(' ', $normalized);

// Теперь ищем по нормализованному запросу
$stmt = $pdo->prepare('SELECT * FROM articles WHERE MATCH(title,content) AGAINST(:q IN BOOLEAN MODE)');
$stmt->execute(['q' => $normalQuery]);
Результаты будут включать все словоформы (глаголы, падежи), что повышает качество поиска.

Сложности: библиотека большая (словари). Для продакшена лучше использовать готовые решения (Elasticsearch с морфологией через плагины).

Форма поиска в PHP - comments

En
Php search form (php)