- •1 Постановка задачи
- •2 Выбор методов и технологий
- •Файловая структура модулей и их функционал
- •Register.Php – модуль регистрации. Файл реализует обработку данных, вводимых пользователем при регистрации.
- •Login.Php – модуль авторизации. Модуль обеспечивает вход зарегистрированных пользователей в систему.
- •Profile.Php – страница профиля. Файл profile.Php отображает персональную страницу пользователя после успешного входа.
- •Logout.Php – модуль выхода из системы. Файл отвечает за завершение пользовательской сессии.
- •Style.Css – таблица стилей. Файл assets/style.Css содержит описания внешнего вида всех элементов интерфейса: карточек, кнопок, форм и полей ввода.
- •Заключение
Style.Css – таблица стилей. Файл assets/style.Css содержит описания внешнего вида всех элементов интерфейса: карточек, кнопок, форм и полей ввода.
Рисунок 2 – Кнопка «Добавить в корзину» в карточке товара
Рисунок 3 – Добавленные в корзину товары
Рисунок 4 – Сформированный заказ
Рисунок 5 – Таблица заказов
Рисунок 6 – Состав заказа
Листинг register.php
<?php
session_start();
error_reporting(E_ALL); ini_set('display_errors', 1);
$dsn = 'pgsql:host=127.0.0.1;port=5432;dbname=interslavic_db';
$user = 'webapp';
$pass = 'PASSWORD';
try {
$pdo = new PDO($dsn, $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
} catch (PDOException $e) {
echo '<!doctype html><meta charset="utf-8"><link rel="stylesheet" href="assets/style.css">';
echo '<body class="auth-page"><div class="auth-card alert err">'
. 'Не удалось подключиться: ' . htmlspecialchars($e->getMessage()) . '</div></body>';
exit;
}
$msg=''; $ok=false;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$fullname = trim($_POST['fullname'] ?? '');
$email = trim($_POST['email'] ?? '');
$login = trim($_POST['login'] ?? '');
$password = $_POST['password'] ?? '';
if ($fullname==='' || $email==='' || $login==='' || $password==='') {
$msg='Заполните все поля.';
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$msg='Некорректный e-mail.';
} else {
$chk=$pdo->prepare('SELECT 1 FROM public.users WHERE email=:e OR login=:l');
$chk->execute([':e'=>$email, ':l'=>$login]);
if ($chk->fetch()) {
$msg='Пользователь с таким e-mail или логином уже существует.';
} else {
try{
$pdo->prepare('INSERT INTO public.users (fullname,email,login,password) VALUES (:f,:e,:l,:p)')
->execute([':f'=>$fullname, ':e'=>$email, ':l'=>$login, ':p'=>password_hash($password, PASSWORD_DEFAULT)]);
$ok=true; $msg='Регистрация прошла успешно! Теперь можно войти.';
}catch(PDOException $ex){
$msg = ($ex->getCode()==='23505') ? 'Такой e-mail или логин уже зарегистрирован.'
: 'Ошибка БД: '.htmlspecialchars($ex->getMessage(), ENT_QUOTES, 'UTF-8');
}
}
}
}
?>
<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
<title>Регистрация — Interslavic</title>
<link rel="stylesheet" href="assets/style.css">
</head>
<body class="auth-page">
<div class="auth-card">
<div class="auth-brand">
<div class="auth-logo">IS</div>
<strong>Interslavic</strong>
</div>
<h2 class="auth-title">Создание аккаунта</h2>
<?php if ($msg): ?>
<div class="alert <?= $ok ? 'ok' : 'err' ?>" style="margin-bottom:10px;">
<?= htmlspecialchars($msg, ENT_QUOTES, 'UTF-8') ?>
</div>
<?php endif; ?>
<form class="auth-form" method="post" autocomplete="off">
<div class="auth-input">
<label>ФИО</label>
<input name="fullname" required value="<?= htmlspecialchars($_POST['fullname'] ?? '', ENT_QUOTES, 'UTF-8') ?>">
</div>
<div class="auth-input">
<label>Email</label>
<input name="email" type="email" required value="<?= htmlspecialchars($_POST['email'] ?? '', ENT_QUOTES, 'UTF-8') ?>">
</div>
<div class="auth-input">
<label>Логин</label>
<input name="login" required value="<?= htmlspecialchars($_POST['login'] ?? '', ENT_QUOTES, 'UTF-8') ?>">
</div>
<div class="auth-input">
<label>Пароль</label>
<input name="password" type="password" required>
</div>
<div class="auth-actions">
<button class="btn" type="submit">Зарегистрироваться</button>
<button class="btn secondary" type="button" onclick="location.href='login.php'">У меня есть аккаунт</button>
</div>
</form>
</div>
</body>
</html>
Листинг login.php
<?php
session_start();
require __DIR__ . '/partials/ui.php';
error_reporting(E_ALL); ini_set('display_errors', 1);
$dsn = 'pgsql:host=127.0.0.1;port=5432;dbname=interslavic_db';
$user = 'webapp';
$pass = 'PASSWORD';
try {
$pdo = new PDO($dsn, $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
} catch (PDOException $e) {
ui_header('Вход');
echo '<div class="container"><div class="card alert err">Не удалось подключиться: '
. htmlspecialchars($e->getMessage()) . '</div></div>';
ui_footer(); exit;
}
$message = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$login = trim($_POST['login'] ?? '');
$password = $_POST['password'] ?? '';
if ($login === '' || $password === '') {
$message = 'Введите логин и пароль.';
} else {
$st = $pdo->prepare('SELECT user_id, fullname, password FROM public.users WHERE login = :l');
$st->execute([':l' => $login]);
$u = $st->fetch();
if ($u && password_verify($password, $u['password'])) {
session_regenerate_id(true);
$_SESSION['user_id'] = $u['user_id'];
$_SESSION['fullname'] = $u['fullname'];
$pdo->prepare('UPDATE public.users SET last_login = now() WHERE user_id = :id')
->execute([':id' => $u['user_id']]);
header('Location: profile.php'); exit;
} else {
$message = 'Неверный логин или пароль.';
}
}
}
?>
<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
<title>Вход — Interslavic</title>
<link rel="stylesheet" href="assets/style.css">
</head>
<body class="auth-page">
<div class="auth-card">
<div class="auth-brand">
<div class="auth-logo">IS</div>
<strong>Interslavic</strong>
</div>
<h2 class="auth-title">Вход в систему</h2>
<?php if ($message): ?>
<div class="alert err" style="margin-bottom:10px;">
<?= htmlspecialchars($message, ENT_QUOTES, 'UTF-8') ?>
</div>
<?php endif; ?>
<form class="auth-form" method="post" autocomplete="off">
<div class="auth-input">
<label>Логин</label>
<input name="login" required>
</div>
<div class="auth-input">
<label>Пароль</label>
<input name="password" type="password" required>
</div>
<div class="auth-actions">
<button class="btn" type="submit">Войти</button>
<button class="btn secondary" type="button" onclick="location.href='register.php'">Создать аккаунт</button>
</div>
</form>
<div class="auth-note">
Забыли пароль? <span class="link">пока не реализовано</span>
</div>
</div>
</body>
</html>
Листинг profile.php
<?php
session_start(); require __DIR__.'/partials/ui.php';
if(!isset($_SESSION['user_id'])){ header('Location: login.php'); exit; }
ui_header('Профиль');
?>
<div class="card">
<div style="display:flex; gap:18px; align-items:center;">
<div class="logo" style="width:64px;height:64px; font-size:22px;">
<?= strtoupper(substr($_SESSION['fullname'],0,1)).strtoupper(substr(strstr($_SESSION['fullname'],' '),1,1)) ?>
</div>
<div>
<h2 style="margin:0"><?= htmlspecialchars($_SESSION['fullname']) ?></h2>
<div style="color:#7a7a7a">Статус: обучающийся</div>
</div>
<div style="margin-left:auto">
<a class="btn secondary" href="logout.php">Выйти</a>
</div>
</div>
</div>
<div class="hero">
<div class="card progress">
<h3>Прогресс</h3>
<div class="pill"><span>Местоимения</span><b>2%</b></div>
<div class="pill"><span>Прилагательные</span><b>54%</b></div>
<div class="pill"><span>Существительные</span><b>100%</b></div>
</div>
<div class="card">
<h3>Продолжить обучение</h3>
<div style="display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:12px; margin-top:12px">
<a class="pill" style="text-decoration:none;color:inherit" href="#"><span>Учебные материалы</span><b>▶</b></a>
<a class="pill" style="text-decoration:none;color:inherit" href="#"><span>Тестирование</span><b>▶</b></a>
<a class="pill" style="text-decoration:none;color:inherit" href="#"><span>Практика</span><b>▶</b></a>
<a class="pill" style="text-decoration:none;color:inherit" href="#"><span>Карточки</span><b>▶</b></a>
</div>
</div>
</div>
<?php ui_footer(); ?>
Листинг logout.php
<?php
session_start();
session_unset();
session_destroy();
header("Location: login.php");
exit;
Листинг cart.php
<?php
session_start();
require __DIR__ . '/partials/ui.php';
require __DIR__ . '/db.php';
if (empty($_SESSION['user_id'])) {
header('Location: login.php');
exit;
}
$userId = (int)$_SESSION['user_id'];
ui_header('Корзина');
$st = $pdo->prepare("
SELECT ci.id as cart_id,
p.id as product_id,
p.title,
p.price,
ci.qty
FROM public.cart_items ci
JOIN public.products p ON p.id = ci.product_id
WHERE ci.user_id = :u
ORDER BY ci.id
");
$st->execute([':u' => $userId]);
$items = $st->fetchAll();
$total = 0;
foreach ($items as $it) {
$lineSum = (float)$it['price'] * (int)$it['qty'];
$total += $lineSum;
}
?>
<section class="card">
<h2>Корзина</h2>
<?php if (!$items): ?>
<p>Ваша корзина пуста.</p>
<?php else: ?>
<table style="width:100%; border-collapse:collapse; margin-top:10px;">
<thead>
<tr style="border-bottom:1px solid #E6E0D9;">
<th style="text-align:left;padding:8px 4px;">Курс</th>
<th style="text-align:left;padding:8px 4px;">Цена</th>
<th style="text-align:left;padding:8px 4px;">Кол-во</th>
<th style="text-align:left;padding:8px 4px;">Сумма</th>
<th style="text-align:left;padding:8px 4px;">Действия</th>
</tr>
</thead>
<tbody>
<?php foreach ($items as $it):
$lineSum = (float)$it['price'] * (int)$it['qty']; ?>
<tr style="border-bottom:1px solid #F0EAE2;">
<td style="padding:6px 4px;"><?= htmlspecialchars($it['title'], ENT_QUOTES, 'UTF-8') ?></td>
<td style="padding:6px 4px;"><?= $it['price'] > 0 ? $it['price'].' ₽' : 'Бесплатно' ?></td>
<td style="padding:6px 4px;"><?= (int)$it['qty'] ?></td>
<td style="padding:6px 4px;"><?= $lineSum > 0 ? $lineSum.' ₽' : '—' ?></td>
<td style="padding:6px 4px;">
<a class="btn secondary small"
href="cart_remove.php?id=<?= (int)$it['cart_id'] ?>"
onclick="return confirm('Удалить курс из корзины?');">
Удалить
</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<p style="margin-top:16px;font-weight:600;">
Итоговая стоимость: <?= $total > 0 ? $total.' ₽' : 'Бесплатно' ?>
</p>
<form action="order_create.php" method="post">
<button type="submit" class="btn">Сформировать заказ</button>
</form>
<?php endif; ?>
</section>
<?php ui_footer(); ?>
Листинг cart_add.php
<?php
session_start();
require __DIR__ . '/db.php';
if (empty($_SESSION['user_id'])) {
header('Location: login.php');
exit;
}
$userId = (int)$_SESSION['user_id'];
$productId = isset($_POST['product_id']) ? (int)$_POST['product_id'] : 0;
if ($productId <= 0) {
header('Location: catalog.php');
exit;
}
$st = $pdo->prepare('SELECT id, price FROM public.products WHERE id = :id');
$st->execute([':id' => $productId]);
$product = $st->fetch();
if (!$product) {
header('Location: catalog.php');
exit;
}
$st = $pdo->prepare('SELECT id, qty FROM public.cart_items
WHERE user_id = :u AND product_id = :p');
$st->execute([':u' => $userId, ':p' => $productId]);
$row = $st->fetch();
if ($row) {
$upd = $pdo->prepare('UPDATE public.cart_items
SET qty = qty + 1
WHERE id = :id');
$upd->execute([':id' => $row['id']]);
} else {
$ins = $pdo->prepare('INSERT INTO public.cart_items (user_id, product_id, qty)
VALUES (:u, :p, 1)');
$ins->execute([':u' => $userId, ':p' => $productId]);
}
header('Location: cart.php');
exit;
Листинг cart_remove.php
<?php
session_start();
require __DIR__ . '/db.php';
if (empty($_SESSION['user_id'])) {
header('Location: login.php');
exit;
}
$userId = (int)$_SESSION['user_id'];
$cartId = isset($_GET['id']) ? (int)$_GET['id'] : 0;
if ($cartId > 0) {
$st = $pdo->prepare('DELETE FROM public.cart_items WHERE id = :id AND user_id = :u');
$st->execute([':id' => $cartId, ':u' => $userId]);
}
header('Location: cart.php');
exit;
Листинг order_create.php
<?php
session_start();
require __DIR__ . '/partials/ui.php';
require __DIR__ . '/db.php';
if (empty($_SESSION['user_id'])) {
header('Location: login.php');
exit;
}
$userId = (int)$_SESSION['user_id'];
$st = $pdo->prepare("
SELECT ci.id as cart_id,
p.id as product_id,
p.title,
p.price,
ci.qty
FROM public.cart_items ci
JOIN public.products p ON p.id = ci.product_id
WHERE ci.user_id = :u
");
$st->execute([':u' => $userId]);
$items = $st->fetchAll();
if (!$items) {
header('Location: cart.php');
exit;
}
$total = 0;
foreach ($items as $it) {
$total += (float)$it['price'] * (int)$it['qty'];
}
try {
$pdo->beginTransaction();
$insOrder = $pdo->prepare("
INSERT INTO public.orders (user_id, total_sum, status)
VALUES (:u, :total, 'new')
RETURNING id
");
$insOrder->execute([':u' => $userId, ':total' => $total]);
$orderId = (int)$insOrder->fetchColumn();
$insItem = $pdo->prepare("
INSERT INTO public.order_items (order_id, product_id, qty, price)
VALUES (:o, :p, :q, :price)
");
foreach ($items as $it) {
$insItem->execute([
':o' => $orderId,
':p' => (int)$it['product_id'],
':q' => (int)$it['qty'],
':price'=> (float)$it['price'],
]);
}
$del = $pdo->prepare('DELETE FROM public.cart_items WHERE user_id = :u');
$del->execute([':u' => $userId]);
$pdo->commit();
} catch (Exception $e) {
$pdo->rollBack();
ui_header('Ошибка заказа');
echo "<section class='card'><p>Не удалось сформировать заказ: "
. htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8')
. "</p></section>";
ui_footer();
exit;
}
ui_header('Заказ сформирован');
?>
<section class="card">
<h2>Заказ №<?= $orderId ?> успешно сформирован</h2>
<p>В ваш заказ входят следующие курсы:</p>
<ul>
<?php foreach ($items as $it): ?>
<li>
<?= htmlspecialchars($it['title'], ENT_QUOTES, 'UTF-8') ?>
— <?= (int)$it['qty'] ?> шт.
(<?= $it['price'] > 0 ? $it['price'].' ₽' : 'Бесплатно' ?>)
</li>
<?php endforeach; ?>
</ul>
<p style="margin-top:10px;font-weight:600;">
Итоговая сумма: <?= $total > 0 ? $total.' ₽' : 'Бесплатно' ?>
</p>
<a href="profile.php" class="btn secondary">Вернуться в профиль</a>
</section>
<?php ui_footer(); ?>
