Товары в PHP-магазине: от базы данных до интерфейса
Основные подходы к управлению товарами на PHP
Наиболее эффективное решение: объектно-ориентированный подход с PDO
Для безопасной и гибкой работы с товарами рекомендуется использовать класс Product, инкапсулирующий логику CRUD через PDO. Это защищает от SQL-инъекций и упрощает поддержку.
class Product {
private $pdo;
public function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
public function getAll() {
$stmt = $this->pdo->query('SELECT * FROM products ORDER BY id DESC');
return $stmt->fetchAll();
}
public function getById($id) {
$stmt = $this->pdo->prepare('SELECT * FROM products WHERE id = ?');
$stmt->execute([$id]);
return $stmt->fetch();
}
public function create($data) {
$sql = 'INSERT INTO products (name, price, description) VALUES (?, ?, ?)';
$stmt = $this->pdo->prepare($sql);
return $stmt->execute([$data['name'], $data['price'], $data['description']]);
}
public function update($id, $data) {
$sql = 'UPDATE products SET name=?, price=?, description=? WHERE id=?';
$stmt = $this->pdo->prepare($sql);
return $stmt->execute([$data['name'], $data['price'], $data['description'], $id]);
}
public function delete($id) {
$stmt = $this->pdo->prepare('DELETE FROM products WHERE id = ?');
return $stmt->execute([$id]);
}
}Shop product php (товары в интернет-магазине php)
Пояснение: каждый метод использует подготовленные выражения (placeholder ?), что исключает внедрение вредоносного кода. Конструктор принимает объект PDO, что позволяет легко менять настройки базы данных.
Типичные ошибки и их решения:
- Ошибка "Undefined index" при обращении к $_POST - необходимо проверять существование ключей через isset() или фильтровать входящие данные.
- Ошибка "SQLSTATE[23000]" при нарушении уникальности - обрабатывать исключения и возвращать пользователю понятное сообщение.
- Проблемы с кодировкой (кракозябры) - устанавливать кодировку подключения: $pdo->exec('SET NAMES utf8').
Как реализовать CRUD без классов, используя процедурный стиль?
Процедурный подход быстрее для небольших проектов. Функции и глобальные переменные могут упростить начальную разработку, но усложняют тестирование.
function getProducts($mysqli) {
$result = $mysqli->query('SELECT * FROM products');
while ($row = $result->fetch_assoc()) {
$products[] = $row;
}
return $products ?? [];
}Проблема: прямое использование query() без экранирования приводит к SQL-инъекциям. Решение - всегда применять prepare() с привязкой параметров.
Типичная ошибка: забыть освободить ресурсы после запроса.
Как ускорить разработку с помощью Laravel?
Laravel предоставляет Eloquent ORM, миграции и валидацию. Модель Product может выглядеть так:
class Product extends Model {
protected $fillable = ['name', 'price', 'description'];
}Контроллер использует Product::all(), Product::find(), Product::create(). Встроенная защита от массового присвоения.
Проблема: высокая сложность для простых сайтов - требуется установка composer, настройка artisan.
Как хранить товары в JSON-файле без базы данных?
Для прототипов или ultra-лёгких решений подходит файловое хранение в JSON.
function loadProducts() {
$json = file_get_contents('products.json');
return json_decode($json, true) ?? [];
}
function saveProducts($products) {
file_put_contents('products.json', json_encode($products, JSON_PRETTY_PRINT));
}Операции CRUD выполняются над массивом в памяти.
Ошибка: одновременные запросы приводят к повреждению файла. Используйте блокировку flock().
Как обеспечить замену источника данных через репозиторий?
Паттерн Repository абстрагирует хранилище. Интерфейс ProductRepositoryInterface и реализации для MySQL, JSON, API.
interface ProductRepositoryInterface {
public function find($id);
public function findAll();
public function save($product);
public function delete($id);
}Код, использующий репозиторий, не зависит от конкретной реализации.
Типичная ошибка: добавление зависимостей от конкретных классов в коде, что нарушает принцип замены.
Расширенные примеры реализации управления товарами
Пример 1: Добавление товара с валидацией и загрузкой изображения
Страница add_product.php содержит форму и скрипт обработки. Код проверяет обязательные поля, загружает файл и вставляет запись в БД.
<?php
require_once 'Product.php';
$pdo = new PDO('mysql:host=localhost;dbname=shop','root','');
$product = new Product($pdo);
$errors = [];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = trim($_POST['name']);
$price = floatval($_POST['price']);
$description = trim($_POST['description']);
if (empty($name)) $errors[] = 'Название обязательно';
if ($price <= 0) $errors[] = 'Цена должна быть больше 0';
// загрузка изображения
$image = '';
if (!empty($_FILES['image']['name'])) {
$ext = pathinfo($_FILES['image']['name'], PATHINFO_EXTENSION);
$newName = uniqid() . '.' . $ext;
move_uploaded_file($_FILES['image']['tmp_name'], 'uploads/' . $newName);
$image = $newName;
}
if (empty($errors)) {
$product->create([
'name' => $name,
'price' => $price,
'description' => $description
]);
// можно сохранить имя изображения в отдельном поле
echo '<p>Товар добавлен успешно</p>';
} else {
echo '<ul><li>' . implode('</li><li>', $errors) . '</li></ul>';
}
}
?>
<form method="post" enctype="multipart/form-data">
<input type="text" name="name" placeholder="Название" required>
<input type="number" step="0.01" name="price" placeholder="Цена" required>
<textarea name="description" placeholder="Описание"></textarea>
<input type="file" name="image" accept="image/*">
<button type="submit">Сохранить</button>
</form>Результат: отображается форма с полями. После заполнения и нажатия кнопки появляется сообщение об успехе или список ошибок. Изображение сохраняется в папку uploads.
Пример 2: Список товаров с пагинацией
Вывод всех товаров постранично с использованием LIMIT и OFFSET. Количество записей на страницу задаётся константой.
<?php
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
$perPage = 10;
$offset = ($page - 1) * $perPage;
$stmt = $pdo->prepare('SELECT * FROM products ORDER BY id DESC LIMIT ? OFFSET ?');
$stmt->bindValue(1, $perPage, PDO::PARAM_INT);
$stmt->bindValue(2, $offset, PDO::PARAM_INT);
$stmt->execute();
$products = $stmt->fetchAll();
$total = $pdo->query('SELECT COUNT(*) FROM products')->fetchColumn();
$pages = ceil($total / $perPage);
?>
<table>
<tr><th>ID</th><th>Название</th><th>Цена</th><th>Действия</th></tr>
<?php foreach ($products as $p): ?>
<tr>
<td><?= $p['id'] ?></td>
<td><?= htmlspecialchars($p['name']) ?></td>
<td><?= $p['price'] ?></td>
<td><a href="edit.php?id=<?= $p['id'] ?>">Редактировать</a> <a href="delete.php?id=<?= $p['id'] ?>" onclick="return confirm('Удалить?')">Удалить</a></td>
</tr>
<?php endforeach; ?>
</table>
<?php for ($i = 1; $i <= $pages; $i++): ?>
<a href="?page=<?= $i ?>"><?= $i ?></a>
<?php endfor; ?>Результат: таблица с товарами, под ней ссылки для переключения страниц. Данные экранированы через htmlspecialchars для предотвращения XSS.
Пример 3: Редактирование товара с предзаполнением формы
Страница edit.php получает id товара, загружает данные в форму и при отправке обновляет запись.
<?php
$id = (int)$_GET['id'];
$item = $product->getById($id);
if (!$item) { die('Товар не найден'); }
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$product->update($id, [
'name' => $_POST['name'],
'price' => $_POST['price'],
'description' => $_POST['description']
]);
header('Location: index.php');
exit;
}
?>
<form method="post">
<input type="text" name="name" value="<?= htmlspecialchars($item['name']) ?>">
<input type="number" step="0.01" name="price" value="<?= $item['price'] ?>">
<textarea name="description"><?= htmlspecialchars($item['description']) ?></textarea>
<button type="submit">Обновить</button>
</form>Результат: форма заполнена текущими данными. После изменения и отправки происходит редирект на список товаров.
Пример 4: Удаление товара с подтверждением и обработкой ошибок
Удаление через отдельный скрипт, который проверяет наличие товара и выполняет DELETE.
<?php
$id = (int)$_GET['id'];
$item = $product->getById($id);
if (!$item) {
die('Товар не найден');
}
if (isset($_GET['confirm']) && $_GET['confirm'] === 'yes') {
$product->delete($id);
header('Location: index.php');
exit;
}
?>
<p>Вы уверены, что хотите удалить товар "<?= htmlspecialchars($item['name']) ?>"?</p>
<a href="?id=<?= $id ?>&confirm=yes">Да, удалить</a>
<a href="index.php">Нет</a>Результат: страница подтверждения с именем товара. При нажатии на "Да, удалить" товар удаляется и происходит редирект. Если товара нет, выводится ошибка.