Страница просмотра с поиском реализация на PHP
Реализация просмотра и поиска на PHP
Основное эффективное решение для страницы просмотра одной записи с функцией поиска других записей использует безопасный подход с подготовленными запросами PDO и разделение логики. Это позволяет избежать SQL-инъекций и корректно обрабатывать пользовательский ввод.
// Файл view.php
$id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);
if ($id === false || $id === null) {
die('Неверный идентификатор');
}
try {
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'user', 'pass');
$stmt = $pdo->prepare('SELECT * FROM articles WHERE id = :id');
$stmt->execute(['id' => $id]);
$article = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$article) {
echo 'Статья не найдена';
exit;
}
} catch (PDOException $e) {
echo 'Ошибка базы данных';
exit;
}
// Вывод деталей статьи
?>
<h3><?= htmlspecialchars($article['title']) ?></h3>
<p><?= nl2br(htmlspecialchars($article['content'])) ?></p>
// Форма поиска (GET запрос на этот же скрипт)
<form method="get">
<input type="text" name="search" placeholder="Поиск статей..." value="<?= htmlspecialchars($_GET['search'] ?? '') ?>">
<button type="submit">Искать</button>
</form>
<?php
$search = trim($_GET['search'] ?? '');
if ($search !== '') {
$stmt = $pdo->prepare('SELECT id, title FROM articles WHERE title LIKE :search LIMIT 10');
$stmt->execute(['search' => '%' . $search . '%']);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
if ($results) {
echo '<ul>';
foreach ($results as $row) {
echo '<li><a href="?id=' . $row['id'] . '">' . htmlspecialchars($row['title']) . '</a></li>';
}
echo '</ul>';
} else {
echo 'Ничего не найдено';
}
}
?>
Пояснение шагов:
- Фильтрация входного параметра id через filter_input с проверкой на целое число.
- Подключение к базе данных через PDO с использованием исключений.
- Подготовленный запрос для выборки статьи по id.
- Экранирование вывода через htmlspecialchars для предотвращения XSS.
- Реализация поиска с параметром search через LIKE с подстановочными знаками.
Типичные ошибки и их решения
Ошибка 1: Прямая вставка переменной в SQL (например, "SELECT * FROM articles WHERE id = $id") приводит к SQL-инъекции. Решение: всегда использовать подготовленные запросы.
Ошибка 2: Отсутствие проверки существования записи. Если id не найден, код выдаст исключение или пустой результат. Решение: выполнить проверку if (!$article).
Ошибка 3: Необработанные символы в выводе (например, HTML-теги в заголовке). Решение: применять htmlspecialchars ко всем выводимым данным.
Вариант 1: Как защитить приложение от SQL-инъекций с помощью PDO?
Использование PDO с подготовленными запросами является стандартом безопасности. Пример для редактирования записи:
$stmt = $pdo->prepare('UPDATE articles SET title = :title WHERE id = :id');
$stmt->execute(['title' => $_POST['title'], 'id' => $id]);
Проблема: Если забыть передать все плейсхолдеры, запрос завершится ошибкой. Решение: проверять соответствие плейсхолдеров и ключей массива.
Вариант 2: Как организовать просмотр записи с помощью mysqli?
Mysqli также поддерживает подготовленные запросы:
$mysqli = new mysqli('localhost', 'user', 'pass', 'test');
$stmt = $mysqli->prepare('SELECT * FROM articles WHERE id = ?');
$stmt->bind_param('i', $id);
$stmt->execute();
$result = $stmt->get_result();
$article = $result->fetch_assoc();
Проблема: Неправильное указание типа параметра (i, s, d) может привести к ошибкам. Решение: строго соответствовать типу данных.
Вариант 3: Как реализовать интерактивный поиск без перезагрузки страницы?
Использование AJAX с отдельным скриптом search.php:
// JavaScript (fetch)
document.getElementById('search-form').addEventListener('submit', async function(e) {
e.preventDefault();
const query = document.querySelector('input[name="search"]').value;
const response = await fetch('search.php?q=' + encodeURIComponent(query));
const data = await response.text();
document.getElementById('results').innerHTML = data;
});
// search.php
$q = $_GET['q'] ?? '';
if ($q) {
$stmt = $pdo->prepare('SELECT id, title FROM articles WHERE title LIKE :q LIMIT 10');
$stmt->execute(['q' => '%' . $q . '%']);
while ($row = $stmt->fetch()) {
echo '<div><a href="?id=' . $row['id'] . '">' . htmlspecialchars($row['title']) . '</a></div>';
}
}
Проблема: XSS при вставке результатов без экранирования. Решение: всегда использовать htmlspecialchars.
Вариант 4: Как сделать быстрый просмотр без защиты (только для обучения)?
Простой конкатенация строк (опасно, не рекомендуется для продакшена):
$id = $_GET['id'];
$query = "SELECT * FROM articles WHERE id = $id";
$result = mysqli_query($conn, $query);
$article = mysqli_fetch_assoc($result);
Проблема: Полная уязвимость для SQL-инъекций. Никогда не использовать в реальных проектах.
Расширенные примеры реализации просмотра и поиска
Пример 1: Полная страница view.php с пагинацией поиска
// view.php - просмотр одной статьи и поиск с пагинацией
$id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);
$search = trim($_GET['search'] ?? '');
$page = max(1, (int)($_GET['page'] ?? 1));
$perPage = 5;
try {
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'user', 'pass', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
]);
// Получение статьи
if ($id) {
$stmt = $pdo->prepare('SELECT * FROM articles WHERE id = :id');
$stmt->execute(['id' => $id]);
$article = $stmt->fetch();
if ($article) {
echo '<h3>' . htmlspecialchars($article['title']) . '</h3>';
echo '<div>' . nl2br(htmlspecialchars($article['content'])) . '</div>';
}
}
// Поиск с пагинацией
if ($search) {
$offset = ($page - 1) * $perPage;
$countStmt = $pdo->prepare('SELECT COUNT(*) FROM articles WHERE title LIKE :search');
$countStmt->execute(['search' => '%' . $search . '%']);
$total = $countStmt->fetchColumn();
$totalPages = ceil($total / $perPage);
$stmt = $pdo->prepare('SELECT id, title, created_at FROM articles WHERE title LIKE :search ORDER BY created_at DESC LIMIT :limit OFFSET :offset');
$stmt->bindValue('search', '%' . $search . '%', PDO::PARAM_STR);
$stmt->bindValue('limit', $perPage, PDO::PARAM_INT);
$stmt->bindValue('offset', $offset, PDO::PARAM_INT);
$stmt->execute();
$results = $stmt->fetchAll();
if ($results) {
echo '<ul>';
foreach ($results as $row) {
echo '<li><a href="?id=' . $row['id'] . '&search=' . urlencode($search) . '">' . htmlspecialchars($row['title']) . '</a> (' . $row['created_at'] . ')</li>';
}
echo '</ul>';
// пагинация
if ($totalPages > 1) {
for ($p = 1; $p <= $totalPages; $p++) {
$active = ($p == $page) ? ' class="fw-bold"' : '';
echo '<a href="?search=' . urlencode($search) . '&page=' . $p . '"' . $active . '>' . $p . '</a> ';
}
}
} else {
echo 'Результатов не найдено';
}
}
} catch (PDOException $e) {
echo 'Ошибка: ' . $e->getMessage();
}
Результат выполнения (пример вывода):
<h3>Заголовок статьи 1</h3> <div>Текст статьи...</div> <ul> <li><a href="?id=5&search=php">Статья о PHP</a> (2024-03-01)</li> <li><a href="?id=8&search=php">PHP для начинающих</a> (2024-02-20)</li> </ul> <a href="?search=php&page=1" class="fw-bold">1</a> <a href="?search=php&page=2">2</a>
Пример 2: Обработка ошибок при отсутствии записи
$id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);
if (!$id) {
http_response_code(400);
echo 'Неверный или отсутствующий идентификатор';
exit;
}
$stmt = $pdo->prepare('SELECT * FROM articles WHERE id = :id');
$stmt->execute(['id' => $id]);
if ($stmt->rowCount() === 0) {
http_response_code(404);
echo 'Запись не найдена';
exit;
}
$article = $stmt->fetch();
Пример 3: Использование полнотекстового поиска (MySQL FULLTEXT)
// Предварительно создать FULLTEXT индекс: ALTER TABLE articles ADD FULLTEXT(title, content);
$search = trim($_GET['search'] ?? '');
if ($search) {
$stmt = $pdo->prepare('SELECT id, title, MATCH(title, content) AGAINST(:search IN BOOLEAN MODE) AS relevance FROM articles WHERE MATCH(title, content) AGAINST(:search2 IN BOOLEAN MODE) ORDER BY relevance DESC LIMIT 10');
$stmt->execute(['search' => $search, 'search2' => $search]);
// вывод результатов
}
Результат (пример):
id: 1, title: "Основы PHP", relevance: 2.5 id: 4, title: "PHP и MySQL", relevance: 1.8
Пример 4: Защита от XSS при выводе результатов поиска
function escape($data) {
return htmlspecialchars($data, ENT_QUOTES, 'UTF-8');
}
// ...
echo '<a href="?id=' . $row['id'] . '">' . escape($row['title']) . '</a>';
Результат (безопасный вывод):
<a href="?id=2">Статья с "кавычками"</a>