Глава 10. Работа с MySQL через PDO
Большая часть PHP-сайтов использует базу данных: новости, пользователи, товары, комментарии, настройки и заказы. Раньше в учебниках часто встречались функции mysql_query(), но они давно устарели. Сегодня для работы с MySQL лучше использовать PDO или mysqli с подготовленными запросами.
Подключение к базе
PDO создаётся через строку подключения DSN. Важно сразу указать кодировку utf8mb4, чтобы корректно хранить русский текст, emoji и специальные символы.
$pdo = new PDO(
'mysql:host=localhost;dbname=site;charset=utf8mb4',
'db_user',
'db_password',
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]
);
SELECT-запрос
Для чтения данных используется query(), если в запросе нет пользовательских параметров. Результат можно получить через fetch() или fetchAll().
$stmt = $pdo->query('SELECT id, title FROM posts ORDER BY id DESC LIMIT 10');
$posts = $stmt->fetchAll();
foreach ($posts as $post) {
echo htmlspecialchars($post['title']) . '<br>';
}
Подготовленные запросы
Если в запрос попадают данные пользователя, нужно использовать prepare() и execute(). Это защищает от SQL-инъекций и делает код предсказуемее.
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email');
$stmt->execute(['email' => $email]);
$user = $stmt->fetch();
Добавление записи
INSERT-запрос тоже должен быть подготовленным. После добавления можно получить ID новой записи через lastInsertId().
$stmt = $pdo->prepare(
'INSERT INTO posts (title, content, created_at) VALUES (:title, :content, NOW())'
);
$stmt->execute([
'title' => $title,
'content' => $content,
]);
$id = $pdo->lastInsertId();
UPDATE и DELETE
Изменение и удаление данных особенно важно выполнять через prepared statements. Никогда не подставляйте ID напрямую из адресной строки без приведения к числу или параметров запроса.
$stmt = $pdo->prepare('UPDATE posts SET title = :title WHERE id = :id');
$stmt->execute([
'title' => $title,
'id' => $id,
]);
Транзакции
Транзакции нужны, когда несколько запросов должны выполниться вместе. Например: создать заказ, добавить товары заказа и списать остатки. Если один шаг упал, всё откатывается.
$pdo->beginTransaction();
try {
// несколько SQL-запросов
$pdo->commit();
} catch (Throwable $e) {
$pdo->rollBack();
throw $e;
}
Минимальная структура таблицы
Для большинства таблиц нужны первичный ключ, даты создания и обновления, индексы для поиска и внешние ключи там, где это возможно. Даже если проект небольшой, структура базы должна быть понятной.
CREATE TABLE posts (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
content TEXT NOT NULL,
created_at DATETIME NOT NULL,
INDEX (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Типичные ошибки
- Использовать старые функции mysql_*.
- Склеивать SQL-строки с данными пользователя.
- Не указывать кодировку соединения.
- Вызывать fetchAll() для огромных выборок.
- Не использовать транзакции для связанных операций.
Итоги
PDO — удобный и современный способ работать с MySQL в PHP. Основные правила просты: подключайтесь с utf8mb4, включайте исключения, используйте подготовленные запросы, экранируйте вывод в HTML и применяйте транзакции там, где данные должны изменяться согласованно.