Основы баз данных и MySQL
Проектирование таблиц, написание SQL-запросов и безопасное подключение к базе данных через PDO.
Проектирование БД и PDO: Безопасность прежде всего
Любой интернет-магазин нуждается в надежном хранилище данных. В нашем курсе мы используем реляционную СУБД MySQL. Проектирование базы данных начинается с нормализации — процесса устранения избыточности данных. Основные таблицы нашего проекта: users, categories, products, orders и order_items.
Для связи PHP и MySQL мы будем использовать PDO (PHP Data Objects). PDO — это универсальный интерфейс для доступа к различным базам данных. Главная причина использования PDO — безопасность. В веб-разработке существует критическая уязвимость, называемая SQL-инъекцией. Она возникает, когда пользовательский ввод напрямую вставляется в SQL-строку. Злоумышленник может ввести в поле поиска '; DROP TABLE users; --, и если код не защищен, база данных будет удалена.
PDO решает эту проблему с помощью Подготавливаемых запросов (Prepared Statements). Вместо подстановки переменных в строку, мы используем плейсхолдеры (например, :price). PDO отправляет сам SQL-запрос и данные отдельно. База данных компилирует запрос, а затем подставляет данные как безопасные значения, что делает SQL-инъекцию невозможной. Мы применим метод Direct Instruction для пошагового разбора подключения.
<?php
// Практика: Подключение и выборка через PDO (Архивный вариант 1, Упражнение 2.1)
// 1. DSN (Data Source Name) - строка с информацией о подключении
$dsn = 'mysql:host=localhost;dbname=shop;charset=utf8mb4';
$user = 'root';
$password = 'secret_password'; // В реальном проекте берется из .env
try {
// 2. Инициализация объекта PDO с настройками
$pdo = new PDO($dsn, $user, $password, [
// Выбрасывать исключения при ошибках SQL (критично для отладки)
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
// Возвращать результаты как ассоциативный массив (ключ - имя колонки)
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
// Отключаем эмуляцию подготовленных запросов для максимальной безопасности
PDO::ATTR_EMULATE_PREPARES => false
]);
// 3. Подготавливаемый запрос (Prepared Statement) с плейсхолдером :price
$stmt = $pdo->prepare('SELECT id, name, price FROM products WHERE price > :price');
// 4. Выполнение запроса с передачей параметров
$stmt->execute(['price' => 100]);
// 5. Получение всех строк в виде массива
$products = $stmt->fetchAll();
foreach ($products as $product) {
echo $product['name'] . ' - $' . $product['price'] . "<br>";
}
} catch (PDOException $e) {
// В production мы бы логировали ошибку, а пользователю показывали "Что-то пошло не так"
die("Ошибка подключения к БД: " . $e->getMessage());
}
?>
Разбор структуры таблиц (Миграции в перспективе)
В приведенном выше примере мы обращаемся к таблице products. Как она должна выглядеть в MySQL? Согласно архивным материалам (Вариант 2, Упражнение 1.1), таблица товаров должна иметь строгую структуру с внешними ключами для связи с категориями. Хотя в будущем мы будем создавать таблицы с помощью миграций Laravel, важно понимать чистый SQL, который генерируется под капотом.
Пример DDL (Data Definition Language) запроса для создания таблицы товаров:
id BIGINT AUTO_INCREMENT PRIMARY KEY— уникальный идентификатор.category_id BIGINT— внешний ключ (Foreign Key), связывающий товар с таблицей categories. Это обеспечивает ссылочную целостность данных: вы не сможете удалить категорию, если к ней привязаны товары (или они удалятся каскадно).name VARCHAR(255) NOT NULL— название товара.price DECIMAL(8, 2) NOT NULL— цена. Использование типа DECIMAL критически важно для денег. Использование FLOAT может привести к ошибкам округления (например, 0.1 + 0.2 = 0.30000000000000004), что недопустимо в e-commerce.
Почему для работы с деньгами в базах данных (например, цена товара) необходимо использовать тип DECIMAL, а не FLOAT?
Флеш-карточки
Какая главная уязвимость предотвращается с помощью Подготавливаемых запросов (Prepared Statements) в PDO?
Нажмите, чтобы увидеть ответ
SQL-инъекции (SQL Injection).
Нажмите, чтобы вернуться