1267 lines
35 KiB
Markdown
1267 lines
35 KiB
Markdown
# AETERNA - Интернет-магазин мебели и интерьера
|
||
|
||
```
|
||
___ ________________ _ _____
|
||
/ | / ____/_ __/ __ \/ | / / |
|
||
/ /| | / __/ / / / /_/ / |/ / /| |
|
||
/ ___ |/ /___ / / / _, _/ /| / ___ |
|
||
/_/ |_/_____/ /_/ /_/ |_/_/ |_/_/ |_|
|
||
|
||
Мебель, которая создает атмосферу
|
||
```
|
||
|
||
Полнофункциональный интернет-магазин мебели с административной панелью, системой авторизации, корзиной покупок и оформлением заказов. Проект разработан на PHP с использованием PostgreSQL в качестве базы данных.
|
||
|
||
---
|
||
|
||
## Оглавление
|
||
|
||
- [Возможности](#возможности)
|
||
- [Технологический стек](#технологический-стек)
|
||
- [Структура проекта](#структура-проекта)
|
||
- [База данных](#база-данных)
|
||
- [Установка и настройка](#установка-и-настройка)
|
||
- [Запуск проекта](#запуск-проекта)
|
||
- [Аутентификация и авторизация](#аутентификация-и-авторизация)
|
||
- [Функционал](#функционал)
|
||
- [API Endpoints](#api-endpoints)
|
||
- [Административная панель](#административная-панель)
|
||
- [Функции и утилиты](#функции-и-утилиты)
|
||
- [Безопасность](#безопасность)
|
||
- [Тестовые аккаунты](#тестовые-аккаунты)
|
||
- [Разработка](#разработка)
|
||
- [Лицензия](#лицензия)
|
||
|
||
---
|
||
|
||
## Возможности
|
||
|
||
### Для покупателей
|
||
- Просмотр каталога товаров с фильтрацией по категориям, цене, цвету и материалу
|
||
- Поиск товаров по названию
|
||
- Детальная страница товара с характеристиками
|
||
- Корзина покупок с возможностью изменения количества
|
||
- Оформление заказа с выбором способа доставки и оплаты
|
||
- Личный кабинет с историей заказов
|
||
- Регистрация и авторизация
|
||
|
||
### Для администраторов
|
||
- Полное управление каталогом товаров (CRUD)
|
||
- Управление категориями и подкатегориями
|
||
- Просмотр и обработка заказов
|
||
- Управление пользователями
|
||
- Статистика продаж
|
||
|
||
---
|
||
|
||
## Технологический стек
|
||
|
||
| Компонент | Технология |
|
||
|-----------|------------|
|
||
| Backend | PHP 7.4+ |
|
||
| База данных | PostgreSQL 12+ |
|
||
| Стилизация | LESS (компилируется в CSS) |
|
||
| JavaScript | jQuery 3.6+ |
|
||
| Иконки | Font Awesome 5.15 |
|
||
| ORM/DB | PDO (PHP Data Objects) |
|
||
|
||
### Зависимости
|
||
- PHP расширения: `pdo`, `pdo_pgsql`, `mbstring`, `json`
|
||
- Браузерная компиляция LESS (less.js)
|
||
|
||
---
|
||
|
||
## Структура проекта
|
||
|
||
```
|
||
cite_practica1/
|
||
├── admin/ # Административная панель
|
||
│ └── index.php # Главная страница админки
|
||
│
|
||
├── api/ # API endpoints (дублируются в public/api/)
|
||
│ ├── auth.php # Аутентификация пользователя
|
||
│ ├── add_to_cart.php # Добавление товара в корзину
|
||
│ ├── cart.php # Управление корзиной (add/update/remove/get/count)
|
||
│ ├── get_cart.php # Получение содержимого корзины
|
||
│ ├── get_cart_count.php # Количество товаров в корзине
|
||
│ ├── get_product.php # Получение информации о товаре
|
||
│ ├── process_order.php # Оформление заказа
|
||
│ ├── register_handler.php # Регистрация пользователя
|
||
│ └── update_cart.php # Обновление количества в корзине
|
||
│
|
||
├── assets/ # Статические ресурсы
|
||
│ ├── css/ # Скомпилированные CSS файлы
|
||
│ ├── img/ # Изображения товаров и интерфейса
|
||
│ ├── js/ # JavaScript файлы
|
||
│ │ ├── checkout.js # Логика оформления заказа
|
||
│ │ └── profile.js # Логика профиля пользователя
|
||
│ └── less/ # LESS исходники стилей
|
||
│ ├── style.less # Основные стили
|
||
│ ├── checkout.less # Стили страницы оформления
|
||
│ └── mixins.less # LESS миксины
|
||
│
|
||
├── config/ # Конфигурация
|
||
│ ├── database.php # Подключение к PostgreSQL
|
||
│ └── check_auth.js # Клиентская проверка авторизации
|
||
│
|
||
├── includes/ # Общие компоненты
|
||
│ ├── auth.php # Функции авторизации
|
||
│ ├── footer.php # Футер сайта
|
||
│ ├── functions.php # Вспомогательные функции
|
||
│ └── header.php # Шапка сайта
|
||
│
|
||
├── migrations/ # SQL миграции базы данных
|
||
│ ├── 001_initial_schema.sql # Начальная схема (users, categories, products)
|
||
│ ├── 002_add_cart_orders.sql # Корзина и заказы
|
||
│ ├── 003_add_product_fields.sql # Дополнительные поля товаров
|
||
│ ├── 004_grant_admin_to_admin_mail.sql # Права администратора
|
||
│ ├── grant_admin.php # Скрипт назначения админа
|
||
│ ├── migrate.php # Раннер миграций
|
||
│ └── seed_data.sql # Тестовые данные
|
||
│
|
||
├── public/ # Публичная директория (точка входа веб-сервера)
|
||
│ ├── index.php # Главная страница
|
||
│ ├── catalog.php # Каталог товаров
|
||
│ ├── product.php # Страница товара
|
||
│ ├── checkout.php # Оформление заказа / корзина
|
||
│ ├── login.php # Страница входа
|
||
│ ├── logout.php # Выход из аккаунта
|
||
│ ├── register.php # Регистрация
|
||
│ ├── services.php # Страница услуг
|
||
│ ├── delivery.php # Доставка и оплата
|
||
│ ├── warranty.php # Гарантия
|
||
│ ├── header_common.php # Общий header
|
||
│ ├── footer.php # Footer
|
||
│ ├── api/ # API endpoints
|
||
│ ├── config/ # Конфигурация
|
||
│ ├── includes/ # Общие компоненты
|
||
│ ├── img/ # Изображения
|
||
│ └── assets/ # Статические ресурсы
|
||
│
|
||
├── uploads/ # Загруженные файлы
|
||
│ └── products/ # Изображения товаров
|
||
│
|
||
├── setup.sh # Скрипт первоначальной настройки
|
||
├── style_for_cite.less # Основной файл стилей
|
||
└── README.md # Этот файл
|
||
```
|
||
|
||
---
|
||
|
||
## База данных
|
||
|
||
### Схема базы данных
|
||
|
||
```mermaid
|
||
erDiagram
|
||
users ||--o{ cart : has
|
||
users ||--o{ orders : places
|
||
categories ||--o{ products : contains
|
||
categories ||--o{ subcategories : has
|
||
products ||--o{ cart : added_to
|
||
products ||--o{ order_items : included_in
|
||
orders ||--|{ order_items : contains
|
||
|
||
users {
|
||
int user_id PK
|
||
varchar email UK
|
||
varchar password_hash
|
||
varchar full_name
|
||
varchar phone
|
||
varchar city
|
||
timestamp created_at
|
||
timestamp updated_at
|
||
timestamp last_login
|
||
boolean is_active
|
||
boolean is_admin
|
||
}
|
||
|
||
categories {
|
||
int category_id PK
|
||
varchar name
|
||
varchar slug UK
|
||
int parent_id FK
|
||
text description
|
||
int sort_order
|
||
boolean is_active
|
||
timestamp created_at
|
||
timestamp updated_at
|
||
}
|
||
|
||
subcategories {
|
||
int subcategory_id PK
|
||
int category_id FK
|
||
varchar name
|
||
varchar slug UK
|
||
text description
|
||
int sort_order
|
||
boolean is_active
|
||
timestamp created_at
|
||
}
|
||
|
||
products {
|
||
int product_id PK
|
||
int category_id FK
|
||
varchar name
|
||
varchar slug UK
|
||
text description
|
||
decimal price
|
||
decimal old_price
|
||
varchar sku UK
|
||
int stock_quantity
|
||
boolean is_available
|
||
boolean is_featured
|
||
decimal rating
|
||
int review_count
|
||
varchar image_url
|
||
varchar color
|
||
varchar material
|
||
varchar card_size
|
||
timestamp created_at
|
||
timestamp updated_at
|
||
}
|
||
|
||
cart {
|
||
int cart_id PK
|
||
int user_id FK
|
||
int product_id FK
|
||
int quantity
|
||
timestamp created_at
|
||
timestamp updated_at
|
||
}
|
||
|
||
orders {
|
||
int order_id PK
|
||
varchar order_number UK
|
||
int user_id FK
|
||
varchar customer_name
|
||
varchar customer_email
|
||
varchar customer_phone
|
||
text delivery_address
|
||
varchar delivery_region
|
||
varchar postal_code
|
||
varchar delivery_method
|
||
varchar payment_method
|
||
decimal subtotal
|
||
decimal discount_amount
|
||
decimal delivery_price
|
||
decimal final_amount
|
||
varchar promo_code
|
||
varchar status
|
||
text notes
|
||
timestamp created_at
|
||
timestamp updated_at
|
||
timestamp completed_at
|
||
}
|
||
|
||
order_items {
|
||
int item_id PK
|
||
int order_id FK
|
||
int product_id FK
|
||
varchar product_name
|
||
decimal product_price
|
||
int quantity
|
||
decimal total_price
|
||
timestamp created_at
|
||
}
|
||
```
|
||
|
||
### Описание таблиц
|
||
|
||
| Таблица | Описание |
|
||
|---------|----------|
|
||
| `users` | Пользователи системы (покупатели и администраторы) |
|
||
| `categories` | Категории товаров (Диваны, Кресла, Кровати и т.д.) |
|
||
| `subcategories` | Подкатегории для более детальной классификации |
|
||
| `products` | Товары с ценами, описанием и характеристиками |
|
||
| `cart` | Корзина покупок (связь пользователь-товар-количество) |
|
||
| `orders` | Заказы с информацией о доставке и оплате |
|
||
| `order_items` | Позиции заказа (товары в заказе) |
|
||
| `migrations` | Служебная таблица для отслеживания миграций |
|
||
|
||
### Миграции
|
||
|
||
Миграции выполняются в следующем порядке:
|
||
|
||
1. `001_initial_schema.sql` - Создание таблиц users, categories, subcategories, products
|
||
2. `002_add_cart_orders.sql` - Добавление таблиц cart, orders, order_items
|
||
3. `003_add_product_fields.sql` - Дополнительные поля для товаров (color, material, card_size)
|
||
4. `004_grant_admin_to_admin_mail.sql` - Назначение прав администратора
|
||
|
||
---
|
||
|
||
## Установка и настройка
|
||
|
||
### Требования
|
||
|
||
- PHP 7.4 или выше
|
||
- PostgreSQL 12 или выше
|
||
- Веб-сервер (Apache/Nginx) или встроенный PHP сервер для разработки
|
||
|
||
### Шаг 1: Клонирование репозитория
|
||
|
||
```bash
|
||
git clone <repository-url>
|
||
cd cite_practica1
|
||
```
|
||
|
||
### Шаг 2: Настройка базы данных
|
||
|
||
Отредактируйте файл `config/database.php`:
|
||
|
||
```php
|
||
<?php
|
||
class Database {
|
||
private static $instance = null;
|
||
private $connection;
|
||
|
||
private function __construct() {
|
||
try {
|
||
$this->connection = new PDO(
|
||
"pgsql:host=ВАШ_ХОСТ;port=5432;dbname=ВАШ_DBNAME",
|
||
"ВАШ_ЛОГИН",
|
||
"ВАШ_ПАРОЛЬ"
|
||
);
|
||
$this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||
$this->connection->exec("SET NAMES 'utf8'");
|
||
} catch(PDOException $e) {
|
||
die("Ошибка подключения: " . $e->getMessage());
|
||
}
|
||
}
|
||
|
||
public static function getInstance() {
|
||
if (self::$instance == null) {
|
||
self::$instance = new Database();
|
||
}
|
||
return self::$instance;
|
||
}
|
||
|
||
public function getConnection() {
|
||
return $this->connection;
|
||
}
|
||
}
|
||
```
|
||
|
||
### Шаг 3: Запуск миграций
|
||
|
||
**Вариант 1: Автоматическая настройка**
|
||
```bash
|
||
chmod +x setup.sh
|
||
./setup.sh
|
||
```
|
||
|
||
**Вариант 2: Ручной запуск миграций**
|
||
```bash
|
||
php migrations/migrate.php
|
||
```
|
||
|
||
**Вариант 3: Загрузка тестовых данных**
|
||
```bash
|
||
php migrations/migrate.php --seed
|
||
```
|
||
|
||
### Шаг 4: Настройка прав доступа
|
||
|
||
```bash
|
||
chmod -R 755 public/
|
||
chmod -R 777 uploads/
|
||
```
|
||
|
||
---
|
||
|
||
## Запуск проекта
|
||
|
||
### Разработка (встроенный PHP сервер)
|
||
|
||
```bash
|
||
cd public
|
||
php -S localhost:8000
|
||
```
|
||
|
||
Затем откройте в браузере: http://localhost:8000
|
||
|
||
### Production (Apache)
|
||
|
||
Пример конфигурации Virtual Host:
|
||
|
||
```apache
|
||
<VirtualHost *:80>
|
||
ServerName aeterna.local
|
||
DocumentRoot /path/to/cite_practica1/public
|
||
|
||
<Directory /path/to/cite_practica1/public>
|
||
AllowOverride All
|
||
Require all granted
|
||
</Directory>
|
||
|
||
ErrorLog ${APACHE_LOG_DIR}/aeterna_error.log
|
||
CustomLog ${APACHE_LOG_DIR}/aeterna_access.log combined
|
||
</VirtualHost>
|
||
```
|
||
|
||
### Production (Nginx)
|
||
|
||
```nginx
|
||
server {
|
||
listen 80;
|
||
server_name aeterna.local;
|
||
root /path/to/cite_practica1/public;
|
||
index index.php;
|
||
|
||
location / {
|
||
try_files $uri $uri/ /index.php?$query_string;
|
||
}
|
||
|
||
location ~ \.php$ {
|
||
fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
|
||
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
|
||
include fastcgi_params;
|
||
}
|
||
|
||
location ~ /\.(ht|git) {
|
||
deny all;
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Аутентификация и авторизация
|
||
|
||
### Архитектура аутентификации
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A[Пользователь] --> B{Авторизован?}
|
||
B -->|Нет| C[Страница входа]
|
||
B -->|Да| D{Роль?}
|
||
|
||
C --> E[Ввод email/пароль]
|
||
E --> F[API: auth.php]
|
||
F --> G{Проверка в БД}
|
||
G -->|Успех| H[Создание сессии]
|
||
G -->|Ошибка| I[Сообщение об ошибке]
|
||
|
||
H --> J[Сохранение в SESSION]
|
||
J --> K[Редирект на каталог]
|
||
|
||
D -->|Пользователь| L[Доступ к каталогу, корзине, заказам]
|
||
D -->|Админ| M[Доступ к админ-панели]
|
||
```
|
||
|
||
### Система сессий
|
||
|
||
При успешной авторизации в сессию сохраняются:
|
||
|
||
```php
|
||
$_SESSION['user_id'] // ID пользователя
|
||
$_SESSION['user_email'] // Email
|
||
$_SESSION['full_name'] // Полное имя
|
||
$_SESSION['isLoggedIn'] // true
|
||
$_SESSION['isAdmin'] // true/false
|
||
$_SESSION['login_time'] // Время входа
|
||
```
|
||
|
||
### Защита страниц
|
||
|
||
```php
|
||
// Требовать авторизацию
|
||
if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) {
|
||
header('Location: login.php?error=auth_required');
|
||
exit();
|
||
}
|
||
|
||
// Требовать права администратора
|
||
if (!isset($_SESSION['isAdmin']) || $_SESSION['isAdmin'] !== true) {
|
||
header('Location: login.php?error=admin_required');
|
||
exit();
|
||
}
|
||
```
|
||
|
||
### Роли пользователей
|
||
|
||
| Роль | Доступ |
|
||
|------|--------|
|
||
| Гость | Главная страница, услуги, доставка, гарантия |
|
||
| Пользователь | + Каталог, корзина, оформление заказов, личный кабинет |
|
||
| Администратор | + Админ-панель, управление товарами, заказами, пользователями |
|
||
|
||
---
|
||
|
||
## Функционал
|
||
|
||
### Пользовательский интерфейс
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
subgraph pages [Страницы]
|
||
A[Главная] --> B[Каталог]
|
||
B --> C[Товар]
|
||
C --> D[Корзина]
|
||
D --> E[Оформление]
|
||
E --> F[Успех]
|
||
end
|
||
|
||
subgraph auth [Авторизация]
|
||
G[Вход] --> H[Регистрация]
|
||
end
|
||
|
||
subgraph info [Информация]
|
||
I[Услуги]
|
||
J[Доставка]
|
||
K[Гарантия]
|
||
L[Контакты]
|
||
end
|
||
```
|
||
|
||
#### Главная страница (`public/index.php`)
|
||
- Hero-секция с призывом к действию
|
||
- Преимущества компании
|
||
- Промо-блоки с акциями
|
||
- Секция "О нас"
|
||
- Слайдер с готовыми решениями
|
||
- Статистика компании
|
||
- FAQ (частые вопросы)
|
||
|
||
#### Каталог товаров (`public/catalog.php`)
|
||
- Сетка товаров с изображениями
|
||
- Фильтрация по:
|
||
- Категориям
|
||
- Диапазону цен
|
||
- Цвету
|
||
- Материалу
|
||
- Поиск по названию
|
||
- Добавление в корзину
|
||
|
||
#### Страница товара (`public/product.php`)
|
||
- Галерея изображений
|
||
- Описание и характеристики
|
||
- Цена и скидка
|
||
- Наличие на складе
|
||
- Кнопка добавления в корзину
|
||
- Рекомендуемые товары
|
||
|
||
#### Корзина (`public/checkout.php`)
|
||
- Список товаров в корзине
|
||
- Изменение количества
|
||
- Удаление товаров
|
||
- Расчет итоговой суммы
|
||
- Применение промокода
|
||
- Форма оформления заказа
|
||
|
||
---
|
||
|
||
## API Endpoints
|
||
|
||
### Аутентификация
|
||
|
||
#### POST `/api/auth.php`
|
||
Авторизация пользователя
|
||
|
||
**Запрос:**
|
||
```json
|
||
{
|
||
"email": "user@example.com",
|
||
"password": "password123"
|
||
}
|
||
```
|
||
|
||
**Ответ (успех):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"redirect": "catalog.php"
|
||
}
|
||
```
|
||
|
||
**Ответ (ошибка):**
|
||
```json
|
||
{
|
||
"success": false,
|
||
"message": "Неверный пароль"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### Корзина
|
||
|
||
#### POST `/api/cart.php?action=add`
|
||
Добавление товара в корзину
|
||
|
||
**Запрос:**
|
||
```json
|
||
{
|
||
"product_id": 1,
|
||
"quantity": 2
|
||
}
|
||
```
|
||
|
||
**Ответ:**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "Товар добавлен в корзину"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### POST `/api/cart.php?action=update`
|
||
Обновление количества товара
|
||
|
||
**Запрос:**
|
||
```json
|
||
{
|
||
"product_id": 1,
|
||
"quantity": 3
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### POST `/api/cart.php?action=remove`
|
||
Удаление товара из корзины
|
||
|
||
**Запрос:**
|
||
```json
|
||
{
|
||
"product_id": 1
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### GET `/api/cart.php?action=get`
|
||
Получение содержимого корзины
|
||
|
||
**Ответ:**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"items": [
|
||
{
|
||
"cart_id": 1,
|
||
"product_id": 5,
|
||
"quantity": 2,
|
||
"name": "Диван ROYALTY",
|
||
"price": 78999,
|
||
"image_url": "img2/6_6.png"
|
||
}
|
||
],
|
||
"total": 157998
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### GET `/api/cart.php?action=count`
|
||
Количество товаров в корзине
|
||
|
||
**Ответ:**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"count": 3
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### Товары
|
||
|
||
#### GET `/api/get_product.php?id=1`
|
||
Получение информации о товаре
|
||
|
||
**Ответ:**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"product": {
|
||
"product_id": 1,
|
||
"name": "Светильник MINNIGHT",
|
||
"description": "Настольный светильник в современном стиле",
|
||
"price": 7999,
|
||
"old_price": 9999,
|
||
"image_url": "img2/1_2.png",
|
||
"color": "Черный",
|
||
"material": "Металл",
|
||
"stock_quantity": 15,
|
||
"is_available": true
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### Заказы
|
||
|
||
#### POST `/api/process_order.php`
|
||
Оформление заказа
|
||
|
||
**Запрос:**
|
||
```json
|
||
{
|
||
"customer_name": "Иван Иванов",
|
||
"customer_email": "ivan@example.com",
|
||
"customer_phone": "+79001234567",
|
||
"delivery_address": "г. Москва, ул. Примерная, д. 1",
|
||
"delivery_method": "courier",
|
||
"payment_method": "card"
|
||
}
|
||
```
|
||
|
||
**Ответ:**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"order_number": "AET-20251216-ABC123",
|
||
"message": "Заказ успешно оформлен"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Административная панель
|
||
|
||
### Доступ
|
||
|
||
URL: `/admin/index.php` или `/public/catalog_admin.php`
|
||
|
||
Требуется авторизация с правами администратора (`is_admin = TRUE`)
|
||
|
||
### Функции
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A[Админ-панель] --> B[Dashboard]
|
||
A --> C[Категории]
|
||
A --> D[Товары]
|
||
A --> E[Заказы]
|
||
A --> F[Пользователи]
|
||
|
||
C --> C1[Список категорий]
|
||
C --> C2[Добавить категорию]
|
||
C --> C3[Редактировать]
|
||
C --> C4[Удалить]
|
||
|
||
D --> D1[Список товаров]
|
||
D --> D2[Добавить товар]
|
||
D --> D3[Редактировать]
|
||
D --> D4[Удалить]
|
||
D --> D5[Загрузка изображений]
|
||
|
||
E --> E1[Список заказов]
|
||
E --> E2[Детали заказа]
|
||
E --> E3[Изменить статус]
|
||
```
|
||
|
||
### Управление категориями
|
||
|
||
- Создание категорий и подкатегорий
|
||
- Редактирование названия и описания
|
||
- Установка порядка сортировки
|
||
- Активация/деактивация
|
||
|
||
### Управление товарами
|
||
|
||
- Добавление новых товаров
|
||
- Редактирование характеристик
|
||
- Загрузка изображений
|
||
- Установка цен и скидок
|
||
- Управление наличием
|
||
|
||
### Управление заказами
|
||
|
||
Статусы заказов:
|
||
| Статус | Описание |
|
||
|--------|----------|
|
||
| `pending` | Ожидает обработки |
|
||
| `processing` | В обработке |
|
||
| `shipped` | Отправлен |
|
||
| `delivered` | Доставлен |
|
||
| `cancelled` | Отменен |
|
||
|
||
---
|
||
|
||
## Функции и утилиты
|
||
|
||
### `includes/functions.php`
|
||
|
||
#### Проверка авторизации
|
||
|
||
```php
|
||
/**
|
||
* Проверка авторизации пользователя
|
||
*/
|
||
function isLoggedIn(): bool {
|
||
return isset($_SESSION['isLoggedIn']) && $_SESSION['isLoggedIn'] === true;
|
||
}
|
||
|
||
/**
|
||
* Проверка прав администратора
|
||
*/
|
||
function isAdmin(): bool {
|
||
return isset($_SESSION['isAdmin']) && $_SESSION['isAdmin'] === true;
|
||
}
|
||
```
|
||
|
||
#### Защита страниц
|
||
|
||
```php
|
||
/**
|
||
* Требовать авторизацию - редирект на login если не авторизован
|
||
*/
|
||
function requireAuth(string $redirectUrl = 'login.php'): void {
|
||
if (!isLoggedIn()) {
|
||
header('Location: ' . $redirectUrl . '?error=auth_required');
|
||
exit();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Требовать права администратора
|
||
*/
|
||
function requireAdmin(string $redirectUrl = 'login.php'): void {
|
||
if (!isAdmin()) {
|
||
header('Location: ' . $redirectUrl . '?error=admin_required');
|
||
exit();
|
||
}
|
||
}
|
||
```
|
||
|
||
#### Получение текущего пользователя
|
||
|
||
```php
|
||
/**
|
||
* Получить текущего пользователя
|
||
*/
|
||
function getCurrentUser(): ?array {
|
||
if (!isLoggedIn()) {
|
||
return null;
|
||
}
|
||
|
||
return [
|
||
'user_id' => $_SESSION['user_id'] ?? 0,
|
||
'email' => $_SESSION['user_email'] ?? '',
|
||
'full_name' => $_SESSION['full_name'] ?? '',
|
||
'is_admin' => isAdmin()
|
||
];
|
||
}
|
||
```
|
||
|
||
#### Форматирование
|
||
|
||
```php
|
||
/**
|
||
* Форматирование цены
|
||
*/
|
||
function formatPrice(float $price): string {
|
||
return number_format($price, 0, '', ' ') . ' ₽';
|
||
}
|
||
|
||
/**
|
||
* Безопасный вывод HTML
|
||
*/
|
||
function e(string $str): string {
|
||
return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
|
||
}
|
||
```
|
||
|
||
#### Генерация идентификаторов
|
||
|
||
```php
|
||
/**
|
||
* Генерация номера заказа
|
||
*/
|
||
function generateOrderNumber(): string {
|
||
return 'AET-' . date('Ymd') . '-' . strtoupper(substr(uniqid(), -6));
|
||
}
|
||
|
||
/**
|
||
* Создание slug из строки
|
||
*/
|
||
function createSlug(string $str): string {
|
||
$slug = transliterate($str);
|
||
$slug = strtolower($slug);
|
||
$slug = preg_replace('/[^a-z0-9]+/', '-', $slug);
|
||
return trim($slug, '-');
|
||
}
|
||
```
|
||
|
||
#### Транслитерация
|
||
|
||
```php
|
||
/**
|
||
* Транслитерация кириллицы в латиницу
|
||
*/
|
||
function transliterate(string $str): string {
|
||
$converter = [
|
||
'а' => 'a', 'б' => 'b', 'в' => 'v', 'г' => 'g', 'д' => 'd',
|
||
'е' => 'e', 'ё' => 'e', 'ж' => 'zh', 'з' => 'z', 'и' => 'i',
|
||
// ... и т.д.
|
||
];
|
||
return strtr($str, $converter);
|
||
}
|
||
```
|
||
|
||
#### Flash-сообщения
|
||
|
||
```php
|
||
/**
|
||
* Установить flash-сообщение
|
||
*/
|
||
function setFlashMessage(string $type, string $message): void {
|
||
$_SESSION['flash_message'] = [
|
||
'type' => $type,
|
||
'message' => $message
|
||
];
|
||
}
|
||
|
||
/**
|
||
* Получить и удалить flash-сообщение
|
||
*/
|
||
function getFlashMessage(): ?array {
|
||
if (isset($_SESSION['flash_message'])) {
|
||
$message = $_SESSION['flash_message'];
|
||
unset($_SESSION['flash_message']);
|
||
return $message;
|
||
}
|
||
return null;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Безопасность
|
||
|
||
### Защита от SQL-инъекций
|
||
|
||
Все запросы к базе данных используют подготовленные выражения (Prepared Statements):
|
||
|
||
```php
|
||
// ПРАВИЛЬНО - безопасно
|
||
$stmt = $db->prepare("SELECT * FROM users WHERE email = ?");
|
||
$stmt->execute([$email]);
|
||
|
||
// НЕПРАВИЛЬНО - уязвимо для SQL-инъекций
|
||
$result = $db->query("SELECT * FROM users WHERE email = '$email'");
|
||
```
|
||
|
||
### Хеширование паролей
|
||
|
||
Пароли хешируются с использованием bcrypt:
|
||
|
||
```php
|
||
// Хеширование при регистрации
|
||
$password_hash = password_hash($password, PASSWORD_DEFAULT);
|
||
|
||
// Проверка при авторизации
|
||
if (password_verify($password, $user['password_hash'])) {
|
||
// Пароль верный
|
||
}
|
||
```
|
||
|
||
### Защита от XSS
|
||
|
||
Все пользовательские данные экранируются при выводе:
|
||
|
||
```php
|
||
// Безопасный вывод
|
||
echo htmlspecialchars($user_input, ENT_QUOTES, 'UTF-8');
|
||
|
||
// Или используя функцию-хелпер
|
||
echo e($user_input);
|
||
```
|
||
|
||
### Защита сессий
|
||
|
||
```php
|
||
// Регенерация ID сессии после авторизации
|
||
session_regenerate_id(true);
|
||
|
||
// Проверка авторизации на каждой странице
|
||
if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) {
|
||
header('Location: login.php');
|
||
exit();
|
||
}
|
||
```
|
||
|
||
### CSRF-защита
|
||
|
||
Рекомендуется добавить токены для форм:
|
||
|
||
```php
|
||
// Генерация токена
|
||
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
||
|
||
// В форме
|
||
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
|
||
|
||
// Проверка
|
||
if ($_POST['csrf_token'] !== $_SESSION['csrf_token']) {
|
||
die('CSRF token mismatch');
|
||
}
|
||
```
|
||
|
||
### Проверка прав доступа
|
||
|
||
```php
|
||
// Проверка на каждой защищенной странице
|
||
if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) {
|
||
header('Location: login.php?error=auth_required');
|
||
exit();
|
||
}
|
||
|
||
// Проверка прав администратора
|
||
if (!isset($_SESSION['isAdmin']) || $_SESSION['isAdmin'] !== true) {
|
||
die('Доступ запрещен');
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Тестовые аккаунты
|
||
|
||
После выполнения `seed_data.sql` доступны следующие аккаунты:
|
||
|
||
### Администратор
|
||
| Поле | Значение |
|
||
|------|----------|
|
||
| Email | `admin@aeterna.ru` |
|
||
| Пароль | `admin123` |
|
||
| Имя | Администратор AETERNA |
|
||
| Права | Полный доступ к админ-панели |
|
||
|
||
### Пользователь
|
||
| Поле | Значение |
|
||
|------|----------|
|
||
| Email | `user@test.com` |
|
||
| Пароль | `user123` |
|
||
| Имя | Тестовый Пользователь |
|
||
| Права | Доступ к каталогу, корзине, заказам |
|
||
|
||
---
|
||
|
||
## Разработка
|
||
|
||
### Создание новой миграции
|
||
|
||
1. Создайте файл `migrations/XXX_название.sql` (где XXX - следующий номер)
|
||
2. Напишите SQL-код
|
||
3. Запустите `php migrations/migrate.php`
|
||
|
||
Пример миграции:
|
||
```sql
|
||
-- 005_add_reviews.sql
|
||
-- Добавление таблицы отзывов
|
||
|
||
CREATE TABLE IF NOT EXISTS reviews (
|
||
review_id SERIAL PRIMARY KEY,
|
||
product_id INTEGER REFERENCES products(product_id) ON DELETE CASCADE,
|
||
user_id INTEGER REFERENCES users(user_id) ON DELETE SET NULL,
|
||
rating INTEGER CHECK (rating >= 1 AND rating <= 5),
|
||
comment TEXT,
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||
);
|
||
|
||
CREATE INDEX IF NOT EXISTS idx_reviews_product ON reviews(product_id);
|
||
```
|
||
|
||
### Стиль кода
|
||
|
||
- Используйте PSR-12 для PHP
|
||
- Именование переменных: `snake_case`
|
||
- Именование функций: `camelCase` или `snake_case`
|
||
- Именование классов: `PascalCase`
|
||
- Комментируйте сложную логику
|
||
- Используйте типизацию где возможно
|
||
|
||
### Структура API endpoint
|
||
|
||
```php
|
||
<?php
|
||
session_start();
|
||
require_once __DIR__ . '/../config/database.php';
|
||
|
||
header('Content-Type: application/json; charset=utf-8');
|
||
|
||
// Проверка авторизации
|
||
if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) {
|
||
echo json_encode(['success' => false, 'message' => 'Требуется авторизация']);
|
||
exit();
|
||
}
|
||
|
||
try {
|
||
$db = Database::getInstance()->getConnection();
|
||
|
||
// Ваша логика здесь
|
||
|
||
echo json_encode(['success' => true, 'data' => $result]);
|
||
|
||
} catch (PDOException $e) {
|
||
echo json_encode(['success' => false, 'message' => 'Ошибка базы данных']);
|
||
}
|
||
```
|
||
|
||
### Добавление новой страницы
|
||
|
||
1. Создайте файл в `public/`
|
||
2. Подключите header и footer
|
||
3. Добавьте проверку авторизации если нужно
|
||
4. Используйте единый стиль оформления
|
||
|
||
```php
|
||
<?php
|
||
session_start();
|
||
require_once __DIR__ . '/../config/database.php';
|
||
|
||
// Проверка авторизации (опционально)
|
||
if (!isset($_SESSION['isLoggedIn']) || $_SESSION['isLoggedIn'] !== true) {
|
||
header('Location: login.php');
|
||
exit();
|
||
}
|
||
?>
|
||
<!DOCTYPE html>
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>AETERNA - Название страницы</title>
|
||
<link rel="stylesheet/less" type="text/css" href="style_for_cite.less">
|
||
<script src="https://cdn.jsdelivr.net/npm/less"></script>
|
||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
|
||
</head>
|
||
<body>
|
||
<?php include 'header_common.php'; ?>
|
||
|
||
<main class="container">
|
||
<!-- Ваш контент -->
|
||
</main>
|
||
|
||
<?php include 'footer.php'; ?>
|
||
</body>
|
||
</html>
|
||
```
|
||
|
||
---
|
||
|
||
## Диаграмма архитектуры
|
||
|
||
```mermaid
|
||
flowchart TB
|
||
subgraph client [Клиент]
|
||
Browser[Браузер]
|
||
end
|
||
|
||
subgraph server [Сервер]
|
||
subgraph public [Public Directory]
|
||
Index[index.php]
|
||
Catalog[catalog.php]
|
||
Product[product.php]
|
||
Checkout[checkout.php]
|
||
Login[login.php]
|
||
end
|
||
|
||
subgraph api [API Layer]
|
||
Auth[auth.php]
|
||
Cart[cart.php]
|
||
Order[process_order.php]
|
||
end
|
||
|
||
subgraph core [Core]
|
||
Config[database.php]
|
||
Functions[functions.php]
|
||
Includes[header/footer]
|
||
end
|
||
|
||
subgraph admin [Admin Panel]
|
||
AdminIndex[admin/index.php]
|
||
end
|
||
end
|
||
|
||
subgraph database [База данных]
|
||
PostgreSQL[(PostgreSQL)]
|
||
end
|
||
|
||
Browser <--> Index
|
||
Browser <--> Catalog
|
||
Browser <--> Product
|
||
Browser <--> Checkout
|
||
Browser <--> Login
|
||
Browser <--> Auth
|
||
Browser <--> Cart
|
||
Browser <--> Order
|
||
Browser <--> AdminIndex
|
||
|
||
Index --> Config
|
||
Catalog --> Config
|
||
Product --> Config
|
||
Checkout --> Config
|
||
Login --> Config
|
||
Auth --> Config
|
||
Cart --> Config
|
||
Order --> Config
|
||
AdminIndex --> Config
|
||
|
||
Config <--> PostgreSQL
|
||
```
|
||
|
||
---
|
||
|
||
## Поток оформления заказа
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant U as Пользователь
|
||
participant B as Браузер
|
||
participant C as checkout.php
|
||
participant API as process_order.php
|
||
participant DB as PostgreSQL
|
||
|
||
U->>B: Открывает корзину
|
||
B->>C: GET /checkout.php
|
||
C->>DB: SELECT cart items
|
||
DB-->>C: Товары корзины
|
||
C-->>B: HTML страница корзины
|
||
|
||
U->>B: Заполняет форму заказа
|
||
U->>B: Нажимает "Оформить"
|
||
B->>API: POST /api/process_order.php
|
||
|
||
API->>DB: BEGIN TRANSACTION
|
||
API->>DB: INSERT INTO orders
|
||
DB-->>API: order_id
|
||
|
||
loop Для каждого товара
|
||
API->>DB: INSERT INTO order_items
|
||
API->>DB: UPDATE products (stock)
|
||
end
|
||
|
||
API->>DB: DELETE FROM cart
|
||
API->>DB: COMMIT
|
||
|
||
API-->>B: JSON success + order_number
|
||
B-->>U: Страница успеха
|
||
```
|
||
|
||
---
|
||
|
||
## Лицензия
|
||
|
||
Этот проект создан в учебных целях.
|
||
|
||
---
|
||
|
||
## Контакты
|
||
|
||
- **Email:** aeterna@mail.ru
|
||
- **Телефон:** +7(912)999-12-23
|
||
- **Telegram:** @aeterna_shop
|
||
|
||
---
|
||
|
||
```
|
||
© 2025 AETERNA. Все права защищены.
|
||
```
|
||
|