Взаимодействие веб-интерфейса с базой данных: полный стек
Основные подходы к интеграции фронтенда и бэкенда
Как получать данные с сервера динамически, без обновления страницы?
Современный способ - использование Fetch API на стороне клиента и PHP с PDO и JSON на сервере. Этот подход обеспечивает асинхронное взаимодействие, разделение логики и безопасность через подготовленные запросы.
Пример: получение списка пользователей из базы MySQL.
// PHP: get_users.php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'user', 'pass');
$stmt = $pdo->query('SELECT id, name FROM users');
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);
header('Content-Type: application/json');
echo json_encode($users);
Web php mysql (веб php mysql)
// JavaScript
fetch('get_users.php')
.then(response => {
if (!response.ok) throw new Error('Ошибка ' + response.status);
return response.json();
})
.then(data => {
// отображение данных
})
.catch(error => console.error(error));
Css javascript php mysql (css javascript php mysql)
Пояснение
PHP-скрипт устанавливает заголовок Content-Type: application/json, чтобы браузер понимал формат. PDO использует подготовленные запросы для предотвращения SQL-инъекций. Fetch возвращает Promise, обработка .then и .catch.
Типичные ошибки и их решение
- Забыли указать header('Content-Type: application/json') - браузер может неправильно интерпретировать ответ, особенно при использовании XMLHttpRequest.
- Не проверяется response.ok - при ошибках сервера (404, 500) промис fetch не переходит в reject, нужно проверять вручную.
- SQL-инъекции при конкатенации строк в запросе - используйте подготовленные запросы.
- Проблемы с кодировкой - в DSN добавьте charset=utf8;mb4 для эмодзи.
Как отправить данные формы асинхронно и сохранить их в базе?
Отправка POST запроса с JSON-телом.
// add_user.php
$input = json_decode(file_get_contents('php://input'), true);
$stmt = $pdo->prepare('INSERT INTO users (name) VALUES (:name)');
$stmt->execute(['name' => $input['name']]);
echo json_encode(['success' => true, 'id' => $pdo->lastInsertId()]);
Html php mysql (html php mysql)
// JavaScript
fetch('add_user.php', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({name: 'John'})
});
Html css php mysql (html css php mysql)
Частая ошибка: не указан заголовок Content-Type или указан неправильно (например, application/x-www-form-urlencoded), тогда PHP не сможет прочитать json_decode. Используйте ключ 'Content-Type': 'application/json'.
Вариант 1: Синхронная отправка формы (традиционный POST)
Этот метод используется, когда не требуется асинхронность: форма отправляется, браузер перезагружает страницу с ответом сервера.
Html css javascript php mysql (html css javascript php mysql)
// save.php
$name = $_POST['name'] ?? '';
$stmt = $pdo->prepare('INSERT INTO users (name) VALUES (?)');
$stmt->execute([$name]);
header('Location: success.php'); // редирект
Php mysql javascripts (php mysql и javascript)
Цель использования
Подходит для простых форм обратной связи, регистрации, где не требуется динамическое обновление страницы.
Проблемы: потеря состояния страницы, большая нагрузка на сервер при каждом действии, отсутствие обратной связи об ошибке без редиректа.
Вариант 2: XMLHttpRequest (XHR) без библиотек
Исторический способ асинхронных запросов. Используется для совместимости со старыми браузерами.
var xhr = new XMLHttpRequest();
xhr.open('GET', 'data.php');
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
var data = JSON.parse(xhr.responseText);
// обработка
}
};
xhr.send();
Php mysql javascript html5 (php mysql javascript html5)
Проблемы: необходимость вручную отслеживать состояния, нет поддержки Promise, сложнее обработка ошибок.
Ошибка: забыли xhr.send() или не проверили readyState. Решение: использовать onload вместо onreadystatechange.
Вариант 3: Использование jQuery для AJAX
Упрощает синтаксис, но требует подключения библиотеки.
$.ajax({
url: 'data.php',
type: 'GET',
dataType: 'json',
success: function(data) {
// работа с data
},
error: function(jqXHR, textStatus, errorThrown) {
console.error(errorThrown);
}
});
Цель: быстрая разработка при уже подключенном jQuery.
Недостатки: зависимость от сторонней библиотеки, увеличение размера страницы, современные браузеры поддерживают fetch.
Вариант 4: Отправка файлов через FormData
Позволяет загружать файлы на сервер асинхронно без обновления страницы.
const formData = new FormData();
formData.append('file', fileInput.files[0]);
fetch('upload.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(result => { console.log(result); });
// upload.php
$uploadDir = 'uploads/';
$fileName = basename($_FILES['file']['name']);
if (move_uploaded_file($_FILES['file']['tmp_name'], $uploadDir . $fileName)) {
echo json_encode(['file' => $fileName]);
} else {
http_response_code(500);
echo json_encode(['error' => 'Ошибка загрузки']);
}
Проблемы: ограничение размера файла в php.ini (upload_max_filesize, post_max_size), проверка типа файла, безопасность (разрешать только определённые расширения).
Расширенные примеры интеграции
Пример 1. Полное CRUD приложение 'Список задач'
Серверный API (api.php) с роутингом:
// api.php
header('Content-Type: application/json');
$pdo = new PDO('mysql:host=localhost;dbname=todo;charset=utf8', 'user', 'pass');
$method = $_SERVER['REQUEST_METHOD'];
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$parts = explode('/', trim($uri, '/'));
$resource = $parts[0] ?? '';
$id = $parts[1] ?? null;
switch ($method) {
case 'GET':
if ($resource === 'tasks' && $id) {
$stmt = $pdo->prepare('SELECT * FROM tasks WHERE id = ?');
$stmt->execute([$id]);
echo json_encode($stmt->fetch(PDO::FETCH_ASSOC));
} elseif ($resource === 'tasks') {
$stmt = $pdo->query('SELECT * FROM tasks ORDER BY created_at DESC');
echo json_encode($stmt->fetchAll(PDO::FETCH_ASSOC));
}
break;
case 'POST':
$input = json_decode(file_get_contents('php://input'), true);
$stmt = $pdo->prepare('INSERT INTO tasks (title, completed) VALUES (?, 0)');
$stmt->execute([$input['title']]);
echo json_encode(['id' => $pdo->lastInsertId()]);
break;
case 'PUT':
$input = json_decode(file_get_contents('php://input'), true);
$stmt = $pdo->prepare('UPDATE tasks SET completed = ? WHERE id = ?');
$stmt->execute([$input['completed'], $id]);
echo json_encode(['updated' => true]);
break;
case 'DELETE':
$stmt = $pdo->prepare('DELETE FROM tasks WHERE id = ?');
$stmt->execute([$id]);
echo json_encode(['deleted' => true]);
break;
default:
http_response_code(405);
echo json_encode(['error' => 'Method not allowed']);
}
Клиентский JavaScript (используя async/await):
const API = '/api/tasks';
async function loadTasks() {
const resp = await fetch(API);
if (!resp.ok) throw new Error('Не удалось загрузить задачи');
const tasks = await resp.json();
renderTasks(tasks);
}
async function addTask(title) {
const resp = await fetch(API, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({title})
});
return resp.json();
}
async function toggleTask(id, completed) {
await fetch(`${API}/${id}`, {
method: 'PUT',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({completed})
});
}
async function deleteTask(id) {
await fetch(`${API}/${id}`, { method: 'DELETE' });
}
// Использование
loadTasks();
Результат: при загрузке страницы список задач появляется без перезагрузки, добавление и удаление происходят мгновенно.
Пример взаимодействия: GET /api/tasks -> JSON [...], POST /api/tasks -> {"id":42}, PUT /api/tasks/42 -> {"updated":true}
Пример 2. Постраничная навигация с AJAX
PHP (tasks_page.php):
$page = $_GET['page'] ?? 1;
$limit = 10;
$offset = ($page - 1) * $limit;
$total = $pdo->query('SELECT COUNT(*) FROM tasks')->fetchColumn();
$stmt = $pdo->prepare('SELECT * FROM tasks LIMIT ? OFFSET ?');
$stmt->bindValue(1, $limit, PDO::PARAM_INT);
$stmt->bindValue(2, $offset, PDO::PARAM_INT);
$stmt->execute();
$tasks = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo json_encode(['tasks' => $tasks, 'total' => $total, 'page' => $page]);
JavaScript:
function loadPage(page) {
fetch('/tasks?page=' + page)
.then(resp => resp.json())
.then(data => {
// отрисовка data.tasks и обновление пагинации с data.total
});
}
document.getElementById('pagination').addEventListener('click', (e) => {
if (e.target.dataset.page) loadPage(e.target.dataset.page);
});
Результат: клик по странице подгружает новые данные без перезагрузки.
Пример 3. Аутентификация с использованием сессий
PHP (login.php):
session_start();
$data = json_decode(file_get_contents('php://input'), true);
$stmt = $pdo->prepare('SELECT * FROM users WHERE login = ? AND password = SHA2(?,256)');
$stmt->execute([$data['login'], $data['password']]);
$user = $stmt->fetch();
if ($user) {
$_SESSION['user_id'] = $user['id'];
echo json_encode(['success' => true]);
} else {
http_response_code(401);
echo json_encode(['error' => 'Неверные данные']);
}
JavaScript при отправке формы входа:
fetch('/login', {
method: 'POST',
credentials: 'include',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({login, password})
}).then(resp => {
if (resp.ok) window.location.href = '/dashboard';
});
Защищённые запросы (например, /profile) проверяют сессию:
session_start();
if (!isset($_SESSION['user_id'])) {
http_response_code(401);
exit;
}
// остальная логика
Проблемы: необходимость настройки CORS для credentials, защита от CSRF с помощью токенов.
Пример 4. Обработка ошибок и отладка
На сервере используйте try-catch:
try {
// ... PDO операции
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(['error' => 'Ошибка базы данных']);
// можно логировать $e->getMessage()
}
На клиенте:
fetch('/api/data')
.then(response => {
if (!response.ok) {
return response.json().then(err => { throw new Error(err.error); });
}
return response.json();
})
.catch(error => {
document.getElementById('error').textContent = error.message;
});
Результат: пользователь видит понятное сообщение вместо непонятной ошибки.