Поиск в PHP с использованием поддействий (search index subaction)

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

Реализация поиска с поддействиями в PHP

Как организовать в PHP поиск по индексу с поддержкой различных поддействий (фильтрация, сортировка, пагинация)?

Наиболее эффективное решение базируется на использовании полнотекстового индекса MySQL в сочетании с параметром subaction, который передаётся через URL или POST. Это позволяет отделить логику поиска от других операций (вывод формы, обработка AJAX).

// config.php
$host = 'localhost'; $db = 'search_db'; $user = 'root'; $pass = '';
$pdo = new PDO("mysql:host=$host;dbname=$db", $user, $pass, [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
]);

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

// search.php?subaction=search&query=php&page=1&sort=relevance
$allowedSubactions = ['search', 'filter', 'sort', 'page'];
$subaction = $_GET['subaction'] ?? 'search';
if (!in_array($subaction, $allowedSubactions)) {
    die('Invalid subaction');
}

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

if ($subaction === 'search' || $subaction === 'filter') {
    $sql = "SELECT *, MATCH(title, content) AGAINST(:query IN BOOLEAN MODE) AS relevance 
            FROM articles 
            WHERE MATCH(title, content) AGAINST(:query IN BOOLEAN MODE)";
    if ($subaction === 'filter') {
        $category = $_GET['category'] ?? '';
        if ($category) {
            $sql .= " AND category = :category";
        }
    }
    $sql .= " ORDER BY relevance DESC LIMIT :limit OFFSET :offset";
    $stmt = $pdo->prepare($sql);
    $stmt->bindValue(':query', $query, PDO::PARAM_STR);
    if ($subaction === 'filter' && !empty($category)) {
        $stmt->bindValue(':category', $category, PDO::PARAM_STR);
    }
    $stmt->bindValue(':limit', $perPage, PDO::PARAM_INT);
    $stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
    $stmt->execute();
    $results = $stmt->fetchAll(PDO::FETCH_ASSOC);
} elseif ($subaction === 'sort') {
    // смена сортировки (например, по дате)
    $order = ($sort === 'date') ? 'created_at DESC' : 'relevance DESC';
    $sql = "SELECT *, MATCH(title, content) AGAINST(:query IN BOOLEAN MODE) AS relevance 
            FROM articles 
            WHERE MATCH(title, content) AGAINST(:query IN BOOLEAN MODE)
            ORDER BY $order LIMIT :limit OFFSET :offset";
    $stmt = $pdo->prepare($sql);
    $stmt->bindValue(':query', $query, PDO::PARAM_STR);
    $stmt->bindValue(':limit', $perPage, PDO::PARAM_INT);
    $stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
    $stmt->execute();
    $results = $stmt->fetchAll(PDO::FETCH_ASSOC);
} elseif ($subaction === 'page') {
    // только пагинация, переиспользование предыдущего запроса
    // аналогично search, но page уже учтён
}

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

Проблемы и их решения

  • SQL-инъекция: использование подготовленных выражений (bindValue) предотвращает внедрение. Не рекомендуется склеивать строку запроса напрямую.
  • Некорректная работа полнотекстового индекса: минимальная длина слова (ft_min_word_len) по умолчанию 4; короткие слова игнорируются. Необходимо настроить в my.cnf или использовать IN BOOLEAN MODE для точного совпадения.
  • Пагинация при больших объёмах данных: LIMIT с большим OFFSET медленный; рекомендуется использовать курсорную пагинацию (WHERE id > last_id LIMIT 10).

Вариант: поиск через оператор LIKE

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

$searchTerm = '%' . str_replace(['%', '_'], ['\\%', '\\_'], $term) . '%';
$sql = "SELECT * FROM articles WHERE title LIKE :term OR content LIKE :term";

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

Проблема: низкая производительность на больших таблицах (полное сканирование), нет релевантности. Решение: использовать только для малых объёмов или в связке с индексами FULLTEXT.

Вариант: внешний поисковый движок (Sphinx, Elasticsearch)

Как обеспечить масштабируемый поиск с поддействиями на уровне сервера?

// Пример для Sphinx через API
$sphinx = new SphinxClient();
$sphinx->SetServer('localhost', 9312);
$sphinx->SetMatchMode(SPH_MATCH_EXTENDED2);
$sphinx->SetSortMode(SPH_SORT_RELEVANCE);
$sphinx->SetLimits($offset, $perPage);
$result = $sphinx->Query($query, 'articles_index');

Проблема: необходимо установить и настроить дополнительное ПО, сложность синхронизации данных. Решение: использовать только при высокой нагрузке.

- Index php act search (действие поиска в php)
- Search php cid (поиск по cid в php)
- Search php keyword (поиск по ключевому слову в php)

Расширенные примеры реализации поиска с поддействиями

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

Пример 1. Полнотекстовый поиск с поддействием filter по категории

Пример
// search.php?subaction=filter&query=php&category=backend&page=1
$query = 'php';
$category = 'backend';
$page = 1;
$perPage = 5;
$offset = ($page - 1) * $perPage;

$sql = "SELECT id, title, LEFT(content, 200) AS snippet,
        MATCH(title, content) AGAINST(:query IN BOOLEAN MODE) AS relevance
        FROM articles
        WHERE MATCH(title, content) AGAINST(:query IN BOOLEAN MODE)
        AND category = :category
        ORDER BY relevance DESC
        LIMIT :limit OFFSET :offset";
$stmt = $pdo->prepare($sql);
$stmt->bindValue(':query', $query, PDO::PARAM_STR);
$stmt->bindValue(':category', $category, PDO::PARAM_STR);
$stmt->bindValue(':limit', $perPage, PDO::PARAM_INT);
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
$stmt->execute();
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);

// Вывод результатов
foreach ($results as $row) {
    echo "

" . htmlspecialchars($row['title']) . "
"; echo htmlspecialchars($row['snippet']) . "
"; echo "Релевантность: " . $row['relevance'] . "

"; }
Результат (пример вывода):

Основы PHP для бэкенда
PHP - один из популярных языков...
Релевантность: 5.42

PHP и MySQL
Соединение с базой данных...
Релевантность: 3.15

Пример 2. Сортировка по дате (поддействие sort)

Пример
// URL: search.php?subaction=sort&query=php&sort=date&page=1
$sort = 'date';
$orders = ['relevance' => 'relevance DESC', 'date' => 'created_at DESC', 'title' => 'title ASC'];
$orderBy = $orders[$sort] ?? $orders['relevance'];

$sql = "SELECT id, title, created_at,
        MATCH(title, content) AGAINST(:query IN BOOLEAN MODE) AS relevance
        FROM articles
        WHERE MATCH(title, content) AGAINST(:query IN BOOLEAN MODE)
        ORDER BY $orderBy
        LIMIT :limit OFFSET :offset";
$stmt = $pdo->prepare($sql);
// ... привязка параметров
$stmt->execute();
$results = $stmt->fetchAll();

// вывод с указанием даты
foreach ($results as $r) {
    echo "

" . htmlspecialchars($r['title']) . " (" . $r['created_at'] . ")

"; }
Результат (сортировка по дате, новые сверху):

Новые возможности PHP 8 (2025-03-10)

Основы PHP для бэкенда (2025-02-20)

Пример 3. Пагинация с поддействием page и передачей состояния

Пример
$totalResults = 47;
$currentPage = 2;
$perPage = 10;
$totalPages = ceil($totalResults / $perPage);

for ($i = 1; $i <= $totalPages; $i++) {
    $active = ($i === $currentPage) ? ' class="active"' : '';
    echo "$i ";
}
Результат (ссылки пагинации):
1 2 3 4 5

Пример 4. Защита от инъекций при построении ORDER BY (динамическое поле)

Пример
$allowedSortFields = ['relevance', 'date', 'title'];
$sortField = $_GET['sort'] ?? 'relevance';
if (!in_array($sortField, $allowedSortFields)) {
    $sortField = 'relevance';
}
$orderBy = ($sortField === 'date') ? 'created_at' : (($sortField === 'title') ? 'title' : 'relevance DESC');
// Используем только белый список, исключаем прямое вставление

Типичная ошибка: прямое включение имени поля из GET в ORDER BY без проверки. Решение: всегда использовать белый список допустимых значений.

Пример 5. Использование AJAX для поддействий без перезагрузки страницы

Пример
// JavaScript (фрагмент)
fetch('search.php?subaction=filter&query='+encodeURIComponent(q)+'&category='+cat)
    .then(res => res.text())
    .then(html => document.getElementById('results').innerHTML = html);

На сервере возвращается только фрагмент HTML, который вставляется в контейнер.

Поддействие поиска в PHP - comments

En
Search index php subaction (php)