КаталогBuilderPHPDocs

Глава 10. Работа с MySQL через PDO

Большая часть PHP-сайтов использует базу данных: новости, пользователи, товары, комментарии, настройки и заказы. Раньше в учебниках часто встречались функции mysql_query(), но они давно устарели. Сегодня для работы с MySQL лучше использовать PDO или mysqli с подготовленными запросами.

Современное примечание. Если в старом проекте ещё есть mysql_* функции, их нужно постепенно заменять. Минимальный современный стандарт — PDO, кодировка UTF-8 и prepared statements для всех пользовательских данных.

Подключение к базе

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 и применяйте транзакции там, где данные должны изменяться согласованно.