Разберём простой, но рабочий пример: как создать форму для добавления материала, автоматически генерировать slug из заголовка (с транслитерацией) и сохранить данные в базу через подготовленный запрос.
Что мы делаем
- HTML‑форма — поля для заголовка, slug, описания, контента, чекбокс публикации.
- JS‑логика — автоматически превращает заголовок в slug (кириллица → латиница), но даёт возможность править вручную. (Slug делает URL понятным для людей и полезным для SEO, обеспечивает уникальность в базе и даёт гибкость за счёт автогенерации с возможностью ручной правки.).
- PHP‑обработчик — валидирует данные, нормализует slug, вставляет запись в БД через
mysqli_prepare(защита от SQL‑инъекций). - Обратная связь — сообщение об успехе или ошибке.
Ниже — твои готовые фрагменты без правок и пояснения, как они работают вместе.
Форма (add-form.php) — интерфейс, который видит пользователь
html
<!DOCTYPE html> <html lang="ru"> <head> <meta charset="utf-8"> <title>Добавить материал</title> <link rel="stylesheet" href="site.css"> </head> <body> <main class="container"> <h2>Добавить материал</h2> <form method="POST" action="add_post.php"> <label for="title">Заголовок (title)</label> <br><input type="text" name="title" id="title" required> <br><label for="slug">Slug (для URL, латиница и дефисы)</label> <br><small><span class="hint">Заполняется автоматически из заголовка (транслитерация). Можно отредактировать вручную.</span></small> <br><input type="text" name="slug" id="slug" required> <br><label for="excerpt">Краткое описание (excerpt, опционально)</label> <br><input type="text" name="excerpt" id="excerpt"> <br><label for="content">Контент (content, можно HTML)</label> <br><textarea name="content" rows="10" cols="50" required></textarea> <br><input type="checkbox" name="is_published" id="is_published" value="1" checked> <label for="content">Опубликовать сразу (is_published)</label> <br><button class="btn-main">Добавить статью</button> </form> </main> <script> const titleInput = document.getElementById('title'); const slugInput = document.getElementById('slug'); let isManualEdit = false; // Если пользователь начал править slug вручную — перестаём его перезаписывать slugInput.addEventListener('input', () => { isManualEdit = true; }); // Таблица транслитерации (кириллица → латиница) const translitMap = { 'а': 'a', 'б': 'b', 'в': 'v', 'г': 'g', 'д': 'd', 'е': 'e', 'ё': 'yo', 'ж': 'zh', 'з': 'z', 'и': 'i', 'й': 'y', 'к': 'k', 'л': 'l', 'м': 'm', 'н': 'n', 'о': 'o', 'п': 'p', 'р': 'r', 'с': 's', 'т': 't', 'у': 'u', 'ф': 'f', 'х': 'h', 'ц': 'ts', 'ч': 'ch', 'ш': 'sh', 'щ': 'sch', 'ъ': '', 'ы': 'y', 'ь': '', 'э': 'e', 'ю': 'yu', 'я': 'ya' }; function transliterate(str) { return str .toLowerCase() .split('') .map(char => translitMap[char] || char) .join(''); } titleInput.addEventListener('input', () => { if (isManualEdit) return; const value = titleInput.value.trim(); if (!value) { slugInput.value = ''; return; } // Транслитерация + очистка от лишних символов let slug = transliterate(value); slug = slug .replace(/[^a-z0-9]+/g, '-') // оставляем только латиницу и цифры, остальное → дефис .replace(/^-+|-+\$/g, '') // убираем дефисы в начале и конце .replace(/-+/g, '-'); // убираем множественные дефисы slugInput.value = slug; }); </script> </body> </html>

Что тут важно для понимания процесса:
method="POST"иaction="add_post.php"задают маршрут: данные уйдут на сервер в отдельный PHP‑файл.- Поля с
name— это ключи, по которым PHP потом вытащит значения из$_POST. - JS не сохраняет данные в базу и не гарантирует формат slug — он только помогает пользователю.
Обработчик (add_post.php) — серверная часть без изменений
Код оставляем как есть, только добавляем короткие комментарии, где что происходит:
php
<?php require 'db_connect.php'; // Проверка соединения с БД if (!$connection || mysqli_connect_errno()) { http_response_code(500); echo 'Ошибка подключения к БД: ' . htmlspecialchars(mysqli_connect_error()); exit; } $title = trim($_POST['title'] ?? ''); $slug = trim($_POST['slug'] ?? ''); $content = $_POST['content'] ?? ''; $meta_desc = $_POST['meta_description'] ?? null; $meta_keys = $_POST['meta_keywords'] ?? null; $is_published = isset($_POST['is_published']) ? 1 : 0; $sort_order = (int)($_POST['sort_order'] ?? 0); // Валидация: title, slug, content обязательны if ($title === '' || $slug === '' || $content === '') { http_response_code(400); echo 'Ошибка: поля title, slug и content обязательны.'; exit; } // Нормализация slug: приводим к единому формату даже если пользователь ввёл что‑то странное $slug = preg_replace('/[^a-z0-9\-]/i', '-', $slug); $slug = preg_replace('/-+/', '-', $slug); $slug = trim($slug, '-'); if ($slug === '') { $slug = 'page-' . time(); } // SQL строго под CREATE TABLE pages // created_at и updated_at не указываем — они заполняются автоматически (DEFAULT CURRENT_TIMESTAMP) $sql = 'INSERT INTO posts (slug, title, content, meta_description, meta_keywords, is_published, sort_order) VALUES (?, ?, ?, ?, ?, ?, ?)'; $stmt = mysqli_prepare($connection, $sql); if (!$stmt) { http_response_code(500); echo 'Ошибка подготовки запроса: ' . htmlspecialchars(mysqli_error($connection)); exit; } // Типы: s=строка, i=число. Порядок строго как в INSERT if (!mysqli_stmt_bind_param($stmt, 'ssssssi', $slug, $title, $content, $meta_desc, $meta_keys, $is_published, $sort_order)) { http_response_code(500); echo 'Ошибка привязки параметров: ' . htmlspecialchars(mysqli_stmt_error($stmt)); mysqli_stmt_close($stmt); exit; } if (mysqli_stmt_execute($stmt)) { $new_id = mysqli_insert_id($connection); echo "Статья успешно добавлена! ID записи: $new_id"; } else { http_response_code(500); echo 'Ошибка при добавлении записи: ' . htmlspecialchars(mysqli_stmt_error($stmt)); } mysqli_stmt_close($stmt); ?>
После добавления записи в базу данных мы видим следующее:
Запись в базе данных:

Как это всё складывается в «первую запись»
- Пользователь открывает форму, вводит заголовок — JS сразу показывает готовый slug.
- Пользователь может поправить slug вручную; тогда автозаполнение отключается.
- После отправки формы данные приходят в
add_post.phpв массиве$_POST. - PHP проверяет, что обязательные поля не пустые.
- PHP дополнительно «чистит» slug регулярками — на случай, если пользователь ввёл недопустимые символы.
- Выполняется подготовленный запрос
INSERT INTO posts …— так в таблице появляется первая строка. - Скрипт выводит сообщение и ID новой записи.

