Связываем фронтенд и бэкенд: HTML, CSS, PHP, MySQL

Раздел: Веб-разработка -> Интеграция фронтенда и бэкенда

Основные подходы к интеграции фронтенда и бэкенда с HTML, CSS, PHP и MySQL

Эффективное решение: подготовленные запросы PDO и вывод данных в HTML с CSS

Для безопасного и производительного взаимодействия фронтенда (HTML+CSS) и бэкенда (PHP+MySQL) рекомендуется использовать расширение PDO (PHP Data Objects). Оно предоставляет единый интерфейс для работы с разными базами данных и, что важнее, защищает от SQL-инъекций через подготовленные запросы.

Пример: вывести список пользователей из таблицы users в виде таблицы с CSS-стилями.

Как выполнить выборку из MySQL через PDO и отобразить данные в HTML?

<?php
$dsn = 'mysql:host=localhost;dbname=testdb;charset=utf8mb4';
$user = 'root';
$pass = '';
try {
    $pdo = new PDO($dsn, $user, $pass, [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    ]);
    $stmt = $pdo->prepare('SELECT id, name, email FROM users ORDER BY id');
    $stmt->execute();
    $users = $stmt->fetchAll();
} catch (PDOException $e) {
    die('Ошибка подключения: ' . $e->getMessage());
}
?>
<!DOCTYPE html>
<html>
<head>
    <style>
        .users-table { border-collapse: collapse; width: 80%; margin: 20px auto; }
        .users-table th, .users-table td { border: 1px solid #ccc; padding: 8px; text-align: left; }
        .users-table th { background-color: #f4f4f4; }
    </style>
</head>
<body>
    <table class="users-table">
        <thead><tr><th>ID</th><th>Имя</th><th>Email</th></tr></thead>
        <tbody>
            <?php foreach ($users as $row): ?>
            <tr>
                <td><?= htmlspecialchars($row['id'], ENT_QUOTES, 'UTF-8') ?></td>
                <td><?= htmlspecialchars($row['name'], ENT_QUOTES, 'UTF-8') ?></td>
                <td><?= htmlspecialchars($row['email'], ENT_QUOTES, 'UTF-8') ?></td>
            </tr>
            <?php endforeach; ?>
        </tbody>
    </table>
</body>
</html>

Возможные проблемы и их решение:

  • Ошибка подключения к MySQL. Проверить имя хоста, базы данных, логин/пароль, а также запущен ли сервер MySQL.
  • SQL-инъекции. Использование подготовленных запросов (как в примере) решает эту проблему. Никогда не вставлять пользовательские данные напрямую в строку запроса.
  • XSS-уязвимости. При выводе данных в HTML всегда применять htmlspecialchars() с указанием кодировки.

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

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

<?php
$link = mysqli_connect('localhost', 'root', '', 'testdb');
if (!$link) {
    die('Ошибка соединения: ' . mysqli_connect_error());
}
mysqli_set_charset($link, 'utf8mb4');
$result = mysqli_query($link, 'SELECT * FROM users');
if ($result) {
    while ($row = mysqli_fetch_assoc($result)) {
        echo 'Имя: ' . htmlspecialchars($row['name']) . '<br>';
    }
    mysqli_free_result($result);
}
mysqli_close($link);
?>

Типичные ошибки:

  • Забыли проверить результат mysqli_connect() - соединение может быть неудачным.
  • Не установлена кодировка соединения - возможны проблемы с кириллицей. Используйте mysqli_set_charset($link, 'utf8mb4').
  • Процедурный API не поддерживает подготовленные запросы в таком же удобном виде, как PDO, что повышает риск инъекций при невнимательном экранировании.

Как отделить логику от представления с помощью шаблонизатора Twig?

Twig позволяет вынести HTML-шаблоны в отдельные файлы, передавая в них данные из PHP. Это улучшает читаемость кода и упрощает совместную работу фронтендера и бэкендера.

<?php
require_once '/path/to/vendor/autoload.php';

$loader = new \Twig\Loader\FilesystemLoader('/path/to/templates');
$twig = new \Twig\Environment($loader, [
    'cache' => '/path/to/compilation_cache',
]);

$pdo = new PDO('mysql:host=localhost;dbname=testdb', 'root', '');
$stmt = $pdo->query('SELECT * FROM products');
$products = $stmt->fetchAll();

echo $twig->render('products.html.twig', ['products' => $products]);
?>

<!-- templates/products.html.twig -->
<h2>Товары</h2>
<ul>
{% for product in products %}
    <li>{{ product.name|e }} - {{ product.price|number_format(2, ',', ' ') }} руб.</li>
{% else %}
    <li>Товаров нет</li>
{% endfor %}
</ul>

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

  • Необходимость установки Twig через Composer. Убедитесь, что путь к автозагрузчику корректен.
  • Кеш шаблонов нужно очищать при изменениях во время разработки. Отключите кеш или используйте auto_reload: true.
  • Экранирование по умолчанию работает, но для атрибутов HTML может потребоваться дополнительный фильтр |e('html_attr').

Как подгружать данные без перезагрузки страницы через fetch (AJAX)?

Фронтенд отправляет запрос к PHP-скрипту, который возвращает JSON, и затем JavaScript обновляет DOM.

<!-- PHP-эндпоинт api/comments.php -->
<?php
header('Content-Type: application/json; charset=utf-8');
$pdo = new PDO('mysql:host=localhost;dbname=blog', 'root', '');
$stmt = $pdo->query('SELECT * FROM comments ORDER BY created_at DESC LIMIT 10');
echo json_encode($stmt->fetchAll(), JSON_UNESCAPED_UNICODE);
?>

<!-- HTML+JS -->
<div id="comments-container"></div>
<script>
    fetch('api/comments.php')
        .then(response => {
            if (!response.ok) throw new Error('Ошибка сервера');
            return response.json();
        })
        .then(comments => {
            const container = document.getElementById('comments-container');
            container.innerHTML = comments.map(c =>
                `<p class="comment"><strong>${c.author}</strong>: ${c.text}</p>`
            ).join('');
        })
        .catch(error => console.error('Ошибка:', error));
</script>

Распространённые ошибки:

  • CORS-ограничения, если PHP и HTML находятся на разных доменах. Добавить заголовок Access-Control-Allow-Origin: * в PHP (с осторожностью).
  • Неверный Content-Type в ответе - JSON не распарсится. Всегда указывать header('Content-Type: application/json').
  • Отсутствие обработки ошибок в JS приводит к молчаливому падению. Используйте .catch().

Как создать простое REST API на PHP для обмена JSON?

API позволяет отделить бэкенд от фронтенда полностью, используя стандартные HTTP методы (GET, POST, PUT, DELETE).

<?php
// index.php (единая точка входа)
$method = $_SERVER['REQUEST_METHOD'];
$path = $_SERVER['PATH_INFO'] ?? '/';

if ($path === '/users' && $method === 'GET') {
    // Получить список пользователей
    $pdo = new PDO('mysql:host=localhost;dbname=api', 'root', '');
    $stmt = $pdo->query('SELECT id, name, email FROM users');
    $users = $stmt->fetchAll(PDO::FETCH_ASSOC);
    echo json_encode($users, JSON_UNESCAPED_UNICODE);
    exit;
}

if ($path === '/users' && $method === 'POST') {
    $input = json_decode(file_get_contents('php://input'), true);
    $name = $input['name'] ?? '';
    $email = $input['email'] ?? '';
    // валидация, затем INSERT
    $stmt = $pdo->prepare('INSERT INTO users (name, email) VALUES (?, ?)');
    $stmt->execute([$name, $email]);
    http_response_code(201);
    echo json_encode(['id' => $pdo->lastInsertId()]);
    exit;
}

http_response_code(404);
echo json_encode(['error' => 'Not Found']);
?>

Сложности реализации:

  • Без маршрутизатора код может стать громоздким. Рекомендуется использовать микрофреймворки (Slim, Lumen).
  • Безопасность: каждый эндпоинт должен проверять права доступа (например, через API-ключи или токены).
  • Обработка PUT/DELETE: данные могут приходить в теле запроса, как и POST, но метод нужно правильно обрабатывать в серверных настройках.

Расширенные примеры интеграции

Полный CRUD: вывод, добавление, редактирование, удаление через PDO

Создадим мини-приложение для управления списком задач (tasks).

Пример
<?php
// config.php
$dsn = 'mysql:host=localhost;dbname=todolist;charset=utf8mb4';
$pdo = new PDO($dsn, 'root', '', [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);

// index.php
require 'config.php';

// Обработка POST (добавление)
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
    if ($_POST['action'] === 'add') {
        $task = trim($_POST['task']);
        if ($task !== '') {
            $stmt = $pdo->prepare('INSERT INTO tasks (title) VALUES (?)');
            $stmt->execute([$task]);
        }
        header('Location: index.php');
        exit;
    }
    if ($_POST['action'] === 'delete' && isset($_POST['id'])) {
        $stmt = $pdo->prepare('DELETE FROM tasks WHERE id = ?');
        $stmt->execute([(int)$_POST['id']]);
        header('Location: index.php');
        exit;
    }
}

// Выборка всех задач
$tasks = $pdo->query('SELECT * FROM tasks ORDER BY created_at DESC')->fetchAll();
?>
<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <style>
        body { font-family: Arial, sans-serif; max-width: 600px; margin: 20px auto; }
        .task-list { list-style: none; padding: 0; }
        .task-item { display: flex; justify-content: space-between; padding: 10px; border-bottom: 1px solid #ddd; }
        .task-item form { display: inline; }
        .add-form { margin-bottom: 20px; }
        .add-form input[type="text"] { padding: 8px; width: 70%; }
        .add-form button { padding: 8px 16px; background: #5cb85c; color: white; border: none; cursor: pointer; }
        .delete-btn { background: #d9534f; color: white; border: none; padding: 5px 10px; cursor: pointer; }
    </style>
    <title>Список задач</title>
</head>
<body>
    <h2>Мои задачи</h2>
    <form class="add-form" method="post">
        <input type="hidden" name="action" value="add">
        <input type="text" name="task" placeholder="Новая задача..." required>
        <button type="submit">Добавить</button>
    </form>
    <ul class="task-list">
        <?php foreach ($tasks as $task): ?>
        <li class="task-item">
            <span><?= htmlspecialchars($task['title'], ENT_QUOTES, 'UTF-8') ?></span>
            <form method="post">
                <input type="hidden" name="action" value="delete">
                <input type="hidden" name="id" value="<?= $task['id'] ?>">
                <button type="submit" class="delete-btn">Удалить</button>
            </form>
        </li>
        <?php endforeach; ?>
    </ul>
</body>
</html>

Результат:

Страница с формой добавления и списком задач, каждая с кнопкой удаления. После отправки формы страница перезагружается с обновлёнными данными.

Динамическая фильтрация через AJAX с передачей параметров

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

Пример
<!-- PHP-эндпоинт get_products.php -->
<?php
header('Content-Type: application/json; charset=utf-8');
$category = isset($_GET['category']) ? $_GET['category'] : '';
if (!$category) {
    echo json_encode(['error' => 'Категория не указана']);
    exit;
}
$pdo = new PDO('mysql:host=localhost;dbname=shop', 'root', '');
$stmt = $pdo->prepare('SELECT id, name, price FROM products WHERE category = ?');
$stmt->execute([$category]);
echo json_encode($stmt->fetchAll(), JSON_UNESCAPED_UNICODE);
?>

<!-- HTML+JS -->
<select id="category-select">
    <option value="">Выберите категорию</option>
    <option value="electronics">Электроника</option>
    <option value="clothing">Одежда</option>
</select>
<div id="product-list"></div>
<script>
    document.getElementById('category-select').addEventListener('change', function() {
        const category = this.value;
        if (!category) {
            document.getElementById('product-list').innerHTML = '';
            return;
        }
        fetch(`get_products.php?category=${encodeURIComponent(category)}`)
            .then(res => res.json())
            .then(products => {
                if (products.error) {
                    document.getElementById('product-list').innerHTML = `<p>${products.error}</p>`;
                    return;
                }
                const html = products.map(p => `<div class="product">
                    <h3>${p.name}</h3>
                    <p>${p.price} руб.</p>
                </div>`).join('');
                document.getElementById('product-list').innerHTML = html;
            })
            .catch(err => console.error(err));
    });
</script>

Результат:

При выборе категории из выпадающего списка в блоке #product-list появляется перечень товаров без перезагрузки страницы. Данные приходят в формате JSON.
- Html css javascript php mysql (html css javascript php mysql)
- Php mysql javascript html5 (php mysql javascript html5)
- Php mysql javascripts (php mysql и javascript)
- Html php mysql (html php mysql)
- Css javascript php mysql (css javascript php mysql)
- Web php mysql (веб php mysql)
- Php mysql javascript css html5 (php mysql javascript css html5)
- Html css php mysql (html css php mysql)
- Php javascript html mysql (php javascript html mysql)

HTML CSS PHP MySQL - comments

En
Html css php mysql (php)