Создание, модификация и удаление MySQL таблиц в PHP
Основной рекомендуемый способ: PDO
Как безопасно и гибко выполнять запросы к таблицам MySQL из PHP?
PDO (PHP Data Objects) предоставляет унифицированный интерфейс и поддержку подготовленных выражений. Это предпочтительный метод для новых проектов.
$dsn = 'mysql:host=localhost;dbname=test;charset=utf8mb4';
$pdo = new PDO($dsn, 'root', '', [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);Создание таблицы:
$pdo->exec("CREATE TABLE IF NOT EXISTS products (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
price DECIMAL(10,2) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");Вставка данных с подготовленным выражением:
$stmt = $pdo->prepare("INSERT INTO products (name, price) VALUES (:name, :price)");
$stmt->execute([':name' => 'Ноутбук', ':price' => 799.99]);Изменение структуры таблицы:
$pdo->exec("ALTER TABLE products ADD COLUMN description TEXT AFTER price");Удаление таблицы:
$pdo->exec("DROP TABLE IF EXISTS products");Типичные ошибки:
- Не указан PDO::ATTR_ERRMODE в исключениях - трудно отловить ошибки.
- Использование неэкранированных переменных в SQL - приводит к инъекциям.
- Забыли проверить успешность exec() - возвращает количество затронутых строк, а не boolean.
- Ошибка подключения: проверьте DSN, имя пользователя и пароль.
Вариант: расширение mysqli (объектный стиль)
Как использовать объектно-ориентированный интерфейс mysqli?
Цель: работа в окружениях, где PDO недоступно или проект уже использует mysqli. Пример подключения и выборки:
$mysqli = new mysqli('localhost', 'root', '', 'test');
if ($mysqli->connect_errno) { echo "Ошибка подключения: {$mysqli->connect_error}"; exit; }
$result = $mysqli->query("SELECT id, name FROM products");
while ($row = $result->fetch_assoc()) { echo $row['name'] . "\n"; }Проблемы: при использовании query() без подготовки возможны SQL-инъекции. Необходимо использовать prepare() и bind_param() для переменных. Решение: $stmt = $mysqli->prepare('SELECT * FROM products WHERE id = ?'); $stmt->bind_param('i', $id); $stmt->execute();.
Вариант: расширение mysqli (процедурный стиль)
Как быстро написать скрипт с mysql функциями?
Цель: простые скрипты, где не требуется ООП. Пример:
$link = mysqli_connect('localhost', 'root', '', 'test');
if (!$link) { die('Ошибка: ' . mysqli_connect_error()); }
$result = mysqli_query($link, "SELECT * FROM products");
while ($row = mysqli_fetch_assoc($result)) { ... }Ошибки: не закрыто соединение, не проверены ошибки запроса. Устаревшие функции mysql_* больше не поддерживаются. Решение: использовать mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT) для автоматической генерации исключений.
Вариант: устаревшее расширение mysql (не рекомендуется)
Почему не стоит использовать функции mysql_* в новых проектах?
Цель: поддержка старого кода, но для нового не использовать. Пример:
$link = mysql_connect('localhost', 'root', '');
mysql_select_db('test', $link);
$result = mysql_query('SELECT * FROM products', $link);
while ($row = mysql_fetch_assoc($result)) { ... }Проблемы: расширение удалено из PHP 7.0, отсутствует поддержка подготовленных выражений, уязвимо к SQL-инъекциям. Решение: мигрировать на PDO или mysqli.
Вариант: использование ORM (например, Eloquent)
Как абстрагироваться от SQL-запросов при работе с таблицами?
Цель: ускорение разработки, автоматическое создание таблиц через миграции, удобное управление связями. Пример модели в Laravel:
class Product extends Model {
protected $table = 'products';
protected $fillable = ['name', 'price'];
}
$product = Product::create(['name' => 'Телефон', 'price' => 599.99]);Проблемы: ORM может скрывать оптимизацию запросов, требуется изучить синтаксис ORM. Для сложных запросов может быть медленнее. Решение: использовать raw-запросы через DB::select() при необходимости.
Дополнительные расширенные примеры работы с таблицами через PDO:
Создание таблицы с внешним ключом и транзакция
try {
$pdo->beginTransaction();
$pdo->exec("CREATE TABLE categories (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL
) ENGINE=InnoDB");
$pdo->exec("CREATE TABLE products (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
category_id INT,
FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE CASCADE
) ENGINE=InnoDB");
$pdo->commit();
} catch (Exception $e) {
$pdo->rollBack();
echo "Ошибка: " . $e->getMessage();
}(таблицы созданы, ошибок нет)
Выборка с использованием fetchColumn
$stmt = $pdo->query("SELECT COUNT(*) FROM products");
$count = $stmt->fetchColumn();
echo "Всего товаров: $count";Всего товаров: 5
Массовая вставка с подготовленными выражениями
$products = [['name' => 'A', 'price' => 10], ['name' => 'B', 'price' => 20]];
$stmt = $pdo->prepare("INSERT INTO products (name, price) VALUES (:name, :price)");
foreach ($products as $p) {
$stmt->execute($p);
}
echo "Добавлено: " . count($products);Добавлено: 2
Обновление с использованием named placeholders
$stmt = $pdo->prepare("UPDATE products SET price = :price WHERE id = :id");
$stmt->execute([':price' => 899.99, ':id' => 1]);
echo $stmt->rowCount() . " строк обновлено";1 строк обновлено
Удаление с условием
$stmt = $pdo->prepare("DELETE FROM products WHERE id = :id");
$stmt->execute([':id' => 10]);
if ($stmt->rowCount() === 0) { echo "Запись не найдена"; }Запись не найдена
Использование fetchAll с режимом FETCH_CLASS
class Product {
public $id;
public $name;
public $price;
}
$stmt = $pdo->query("SELECT id, name, price FROM products");
$products = $stmt->fetchAll(PDO::FETCH_CLASS, 'Product');
foreach ($products as $p) { echo $p->name . " - " . $p->price; }Ноутбук - 799.99
Запрос с JOIN и подготовленными параметрами
$stmt = $pdo->prepare("SELECT p.name, c.name AS category FROM products p JOIN categories c ON p.category_id = c.id WHERE c.id = ?");
$stmt->execute([1]);
$rows = $stmt->fetchAll();
print_r($rows);Array ( [0] => Array ( [name] => Ноутбук [category] => Электроника ) )
Получение ID последней вставленной записи
$pdo->exec("INSERT INTO categories (name) VALUES ('Новая категория')");
$id = $pdo->lastInsertId();
echo "ID новой категории: $id";ID новой категории: 3