[MVC] Полная миграция на MVC архитектуру
- Создано ядро MVC: App, Router, Controller, Model, View, Database - Созданы модели: User, Product, Category, Cart, Order - Созданы контроллеры: Home, Auth, Product, Cart, Order, Page, Admin - Созданы layouts и partials для представлений - Добавлены все views для страниц - Настроена маршрутизация с чистыми URL - Обновлена конфигурация Docker и Apache для mod_rewrite - Добавлена единая точка входа public/index.php
This commit is contained in:
374
app/Controllers/AdminController.php
Normal file
374
app/Controllers/AdminController.php
Normal file
@@ -0,0 +1,374 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Core\Controller;
|
||||
use App\Models\Product;
|
||||
use App\Models\Category;
|
||||
use App\Models\Order;
|
||||
use App\Models\User;
|
||||
|
||||
/**
|
||||
* AdminController - контроллер админ-панели
|
||||
*/
|
||||
class AdminController extends Controller
|
||||
{
|
||||
private Product $productModel;
|
||||
private Category $categoryModel;
|
||||
private Order $orderModel;
|
||||
private User $userModel;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->productModel = new Product();
|
||||
$this->categoryModel = new Category();
|
||||
$this->orderModel = new Order();
|
||||
$this->userModel = new User();
|
||||
}
|
||||
|
||||
/**
|
||||
* Дашборд
|
||||
*/
|
||||
public function dashboard(): void
|
||||
{
|
||||
$this->requireAdmin();
|
||||
|
||||
$stats = [
|
||||
'total_products' => $this->productModel->count(),
|
||||
'active_products' => $this->productModel->count(['is_available' => true]),
|
||||
'total_orders' => $this->orderModel->count(),
|
||||
'total_users' => $this->userModel->count(),
|
||||
'revenue' => $this->orderModel->getStats()['revenue']
|
||||
];
|
||||
|
||||
$this->view('admin/dashboard', [
|
||||
'stats' => $stats,
|
||||
'user' => $this->getCurrentUser()
|
||||
], 'admin');
|
||||
}
|
||||
|
||||
// ========== Товары ==========
|
||||
|
||||
/**
|
||||
* Список товаров
|
||||
*/
|
||||
public function products(): void
|
||||
{
|
||||
$this->requireAdmin();
|
||||
|
||||
$showAll = $this->getQuery('show_all') === '1';
|
||||
$products = $this->productModel->getAllForAdmin($showAll);
|
||||
|
||||
$this->view('admin/products/index', [
|
||||
'products' => $products,
|
||||
'showAll' => $showAll,
|
||||
'message' => $this->getQuery('message'),
|
||||
'error' => $this->getQuery('error'),
|
||||
'user' => $this->getCurrentUser()
|
||||
], 'admin');
|
||||
}
|
||||
|
||||
/**
|
||||
* Форма добавления товара
|
||||
*/
|
||||
public function addProduct(): void
|
||||
{
|
||||
$this->requireAdmin();
|
||||
|
||||
$categories = $this->categoryModel->getActive();
|
||||
|
||||
$this->view('admin/products/form', [
|
||||
'product' => null,
|
||||
'categories' => $categories,
|
||||
'action' => 'add',
|
||||
'user' => $this->getCurrentUser()
|
||||
], 'admin');
|
||||
}
|
||||
|
||||
/**
|
||||
* Сохранение нового товара
|
||||
*/
|
||||
public function storeProduct(): void
|
||||
{
|
||||
$this->requireAdmin();
|
||||
|
||||
$data = [
|
||||
'name' => $this->getPost('name'),
|
||||
'category_id' => (int) $this->getPost('category_id'),
|
||||
'description' => $this->getPost('description'),
|
||||
'price' => (float) $this->getPost('price'),
|
||||
'old_price' => $this->getPost('old_price') ? (float) $this->getPost('old_price') : null,
|
||||
'sku' => $this->getPost('sku'),
|
||||
'stock_quantity' => (int) $this->getPost('stock_quantity', 0),
|
||||
'is_available' => $this->getPost('is_available') ? true : false,
|
||||
'is_featured' => $this->getPost('is_featured') ? true : false,
|
||||
'image_url' => $this->getPost('image_url'),
|
||||
'color' => $this->getPost('color'),
|
||||
'material' => $this->getPost('material'),
|
||||
'card_size' => $this->getPost('card_size', 'small')
|
||||
];
|
||||
|
||||
try {
|
||||
$this->productModel->createProduct($data);
|
||||
$this->redirect('/admin/products?message=' . urlencode('Товар успешно добавлен'));
|
||||
} catch (\Exception $e) {
|
||||
$this->redirect('/admin/products/add?error=' . urlencode($e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Форма редактирования товара
|
||||
*/
|
||||
public function editProduct(int $id): void
|
||||
{
|
||||
$this->requireAdmin();
|
||||
|
||||
$product = $this->productModel->find($id);
|
||||
|
||||
if (!$product) {
|
||||
$this->redirect('/admin/products?error=' . urlencode('Товар не найден'));
|
||||
return;
|
||||
}
|
||||
|
||||
$categories = $this->categoryModel->getActive();
|
||||
|
||||
$this->view('admin/products/form', [
|
||||
'product' => $product,
|
||||
'categories' => $categories,
|
||||
'action' => 'edit',
|
||||
'user' => $this->getCurrentUser()
|
||||
], 'admin');
|
||||
}
|
||||
|
||||
/**
|
||||
* Обновление товара
|
||||
*/
|
||||
public function updateProduct(int $id): void
|
||||
{
|
||||
$this->requireAdmin();
|
||||
|
||||
$data = [
|
||||
'name' => $this->getPost('name'),
|
||||
'category_id' => (int) $this->getPost('category_id'),
|
||||
'description' => $this->getPost('description'),
|
||||
'price' => (float) $this->getPost('price'),
|
||||
'old_price' => $this->getPost('old_price') ? (float) $this->getPost('old_price') : null,
|
||||
'stock_quantity' => (int) $this->getPost('stock_quantity', 0),
|
||||
'is_available' => $this->getPost('is_available') ? true : false,
|
||||
'image_url' => $this->getPost('image_url'),
|
||||
'color' => $this->getPost('color'),
|
||||
'material' => $this->getPost('material')
|
||||
];
|
||||
|
||||
try {
|
||||
$this->productModel->updateProduct($id, $data);
|
||||
$this->redirect('/admin/products?message=' . urlencode('Товар обновлен'));
|
||||
} catch (\Exception $e) {
|
||||
$this->redirect('/admin/products/edit/' . $id . '?error=' . urlencode($e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Удаление товара (делаем недоступным)
|
||||
*/
|
||||
public function deleteProduct(int $id): void
|
||||
{
|
||||
$this->requireAdmin();
|
||||
|
||||
$this->productModel->update($id, ['is_available' => false]);
|
||||
$this->redirect('/admin/products?message=' . urlencode('Товар скрыт'));
|
||||
}
|
||||
|
||||
// ========== Категории ==========
|
||||
|
||||
/**
|
||||
* Список категорий
|
||||
*/
|
||||
public function categories(): void
|
||||
{
|
||||
$this->requireAdmin();
|
||||
|
||||
$categories = $this->categoryModel->getAllWithProductCount();
|
||||
|
||||
$this->view('admin/categories/index', [
|
||||
'categories' => $categories,
|
||||
'message' => $this->getQuery('message'),
|
||||
'error' => $this->getQuery('error'),
|
||||
'user' => $this->getCurrentUser()
|
||||
], 'admin');
|
||||
}
|
||||
|
||||
/**
|
||||
* Форма добавления категории
|
||||
*/
|
||||
public function addCategory(): void
|
||||
{
|
||||
$this->requireAdmin();
|
||||
|
||||
$parentCategories = $this->categoryModel->getParent();
|
||||
|
||||
$this->view('admin/categories/form', [
|
||||
'category' => null,
|
||||
'parentCategories' => $parentCategories,
|
||||
'action' => 'add',
|
||||
'user' => $this->getCurrentUser()
|
||||
], 'admin');
|
||||
}
|
||||
|
||||
/**
|
||||
* Сохранение категории
|
||||
*/
|
||||
public function storeCategory(): void
|
||||
{
|
||||
$this->requireAdmin();
|
||||
|
||||
$data = [
|
||||
'name' => $this->getPost('name'),
|
||||
'parent_id' => $this->getPost('parent_id') ? (int) $this->getPost('parent_id') : null,
|
||||
'description' => $this->getPost('description'),
|
||||
'sort_order' => (int) $this->getPost('sort_order', 0),
|
||||
'is_active' => $this->getPost('is_active') ? true : false
|
||||
];
|
||||
|
||||
try {
|
||||
$this->categoryModel->createCategory($data);
|
||||
$this->redirect('/admin/categories?message=' . urlencode('Категория добавлена'));
|
||||
} catch (\Exception $e) {
|
||||
$this->redirect('/admin/categories/add?error=' . urlencode($e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Форма редактирования категории
|
||||
*/
|
||||
public function editCategory(int $id): void
|
||||
{
|
||||
$this->requireAdmin();
|
||||
|
||||
$category = $this->categoryModel->find($id);
|
||||
|
||||
if (!$category) {
|
||||
$this->redirect('/admin/categories?error=' . urlencode('Категория не найдена'));
|
||||
return;
|
||||
}
|
||||
|
||||
$parentCategories = $this->categoryModel->getParent();
|
||||
|
||||
$this->view('admin/categories/form', [
|
||||
'category' => $category,
|
||||
'parentCategories' => $parentCategories,
|
||||
'action' => 'edit',
|
||||
'user' => $this->getCurrentUser()
|
||||
], 'admin');
|
||||
}
|
||||
|
||||
/**
|
||||
* Обновление категории
|
||||
*/
|
||||
public function updateCategory(int $id): void
|
||||
{
|
||||
$this->requireAdmin();
|
||||
|
||||
$data = [
|
||||
'name' => $this->getPost('name'),
|
||||
'parent_id' => $this->getPost('parent_id') ? (int) $this->getPost('parent_id') : null,
|
||||
'description' => $this->getPost('description'),
|
||||
'sort_order' => (int) $this->getPost('sort_order', 0),
|
||||
'is_active' => $this->getPost('is_active') ? true : false
|
||||
];
|
||||
|
||||
try {
|
||||
$this->categoryModel->updateCategory($id, $data);
|
||||
$this->redirect('/admin/categories?message=' . urlencode('Категория обновлена'));
|
||||
} catch (\Exception $e) {
|
||||
$this->redirect('/admin/categories/edit/' . $id . '?error=' . urlencode($e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Удаление категории
|
||||
*/
|
||||
public function deleteCategory(int $id): void
|
||||
{
|
||||
$this->requireAdmin();
|
||||
|
||||
$result = $this->categoryModel->safeDelete($id);
|
||||
|
||||
if ($result['deleted']) {
|
||||
$this->redirect('/admin/categories?message=' . urlencode('Категория удалена'));
|
||||
} else {
|
||||
$msg = $result['reason'] === 'has_products'
|
||||
? 'Категория скрыта (содержит товары)'
|
||||
: 'Категория скрыта (имеет дочерние категории)';
|
||||
$this->redirect('/admin/categories?message=' . urlencode($msg));
|
||||
}
|
||||
}
|
||||
|
||||
// ========== Заказы ==========
|
||||
|
||||
/**
|
||||
* Список заказов
|
||||
*/
|
||||
public function orders(): void
|
||||
{
|
||||
$this->requireAdmin();
|
||||
|
||||
$orders = $this->orderModel->getAllForAdmin();
|
||||
|
||||
$this->view('admin/orders/index', [
|
||||
'orders' => $orders,
|
||||
'user' => $this->getCurrentUser()
|
||||
], 'admin');
|
||||
}
|
||||
|
||||
/**
|
||||
* Детали заказа
|
||||
*/
|
||||
public function orderDetails(int $id): void
|
||||
{
|
||||
$this->requireAdmin();
|
||||
|
||||
$order = $this->orderModel->getWithDetails($id);
|
||||
|
||||
if (!$order) {
|
||||
$this->redirect('/admin/orders?error=' . urlencode('Заказ не найден'));
|
||||
return;
|
||||
}
|
||||
|
||||
$this->view('admin/orders/details', [
|
||||
'order' => $order,
|
||||
'user' => $this->getCurrentUser()
|
||||
], 'admin');
|
||||
}
|
||||
|
||||
/**
|
||||
* Обновление статуса заказа
|
||||
*/
|
||||
public function updateOrderStatus(int $id): void
|
||||
{
|
||||
$this->requireAdmin();
|
||||
|
||||
$status = $this->getPost('status');
|
||||
$this->orderModel->updateStatus($id, $status);
|
||||
|
||||
$this->redirect('/admin/orders/' . $id);
|
||||
}
|
||||
|
||||
// ========== Пользователи ==========
|
||||
|
||||
/**
|
||||
* Список пользователей
|
||||
*/
|
||||
public function users(): void
|
||||
{
|
||||
$this->requireAdmin();
|
||||
|
||||
$users = $this->userModel->getAllPaginated();
|
||||
|
||||
$this->view('admin/users/index', [
|
||||
'users' => $users,
|
||||
'user' => $this->getCurrentUser()
|
||||
], 'admin');
|
||||
}
|
||||
}
|
||||
|
||||
217
app/Controllers/AuthController.php
Normal file
217
app/Controllers/AuthController.php
Normal file
@@ -0,0 +1,217 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Core\Controller;
|
||||
use App\Models\User;
|
||||
|
||||
/**
|
||||
* AuthController - контроллер авторизации
|
||||
*/
|
||||
class AuthController extends Controller
|
||||
{
|
||||
private User $userModel;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->userModel = new User();
|
||||
}
|
||||
|
||||
/**
|
||||
* Форма входа
|
||||
*/
|
||||
public function loginForm(): void
|
||||
{
|
||||
if ($this->isAuthenticated()) {
|
||||
$this->redirect('/catalog');
|
||||
}
|
||||
|
||||
$redirect = $this->getQuery('redirect', '/catalog');
|
||||
|
||||
$this->view('auth/login', [
|
||||
'redirect' => $redirect,
|
||||
'error' => $this->getFlash('error'),
|
||||
'success' => $this->getFlash('success')
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Обработка входа
|
||||
*/
|
||||
public function login(): void
|
||||
{
|
||||
$email = $this->getPost('email', '');
|
||||
$password = $this->getPost('password', '');
|
||||
$redirect = $this->getPost('redirect', '/catalog');
|
||||
|
||||
if (empty($email) || empty($password)) {
|
||||
$this->json([
|
||||
'success' => false,
|
||||
'message' => 'Заполните все поля'
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
$user = $this->userModel->authenticate($email, $password);
|
||||
|
||||
if (!$user) {
|
||||
$this->json([
|
||||
'success' => false,
|
||||
'message' => 'Неверный email или пароль'
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Устанавливаем сессию
|
||||
$this->setSession($user);
|
||||
|
||||
$this->json([
|
||||
'success' => true,
|
||||
'redirect' => $redirect
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Форма регистрации
|
||||
*/
|
||||
public function registerForm(): void
|
||||
{
|
||||
if ($this->isAuthenticated()) {
|
||||
$this->redirect('/catalog');
|
||||
}
|
||||
|
||||
$this->view('auth/register', [
|
||||
'errors' => $_SESSION['registration_errors'] ?? [],
|
||||
'old' => $_SESSION['old_data'] ?? [],
|
||||
'success' => $_SESSION['registration_success'] ?? null
|
||||
]);
|
||||
|
||||
// Очищаем flash данные
|
||||
unset($_SESSION['registration_errors']);
|
||||
unset($_SESSION['old_data']);
|
||||
unset($_SESSION['registration_success']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Обработка регистрации
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
$errors = [];
|
||||
|
||||
$fullName = trim($this->getPost('fio', ''));
|
||||
$city = trim($this->getPost('city', ''));
|
||||
$email = trim($this->getPost('email', ''));
|
||||
$phone = trim($this->getPost('phone', ''));
|
||||
$password = $this->getPost('password', '');
|
||||
$confirmPassword = $this->getPost('confirm-password', '');
|
||||
$privacy = $this->getPost('privacy');
|
||||
|
||||
// Валидация
|
||||
if (empty($fullName) || strlen($fullName) < 3) {
|
||||
$errors[] = 'ФИО должно содержать минимум 3 символа';
|
||||
}
|
||||
|
||||
if (empty($city) || strlen($city) < 2) {
|
||||
$errors[] = 'Введите корректное название города';
|
||||
}
|
||||
|
||||
if (empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
$errors[] = 'Введите корректный email адрес';
|
||||
}
|
||||
|
||||
if (empty($phone)) {
|
||||
$errors[] = 'Введите номер телефона';
|
||||
}
|
||||
|
||||
if (empty($password) || strlen($password) < 6) {
|
||||
$errors[] = 'Пароль должен содержать минимум 6 символов';
|
||||
}
|
||||
|
||||
if ($password !== $confirmPassword) {
|
||||
$errors[] = 'Пароли не совпадают';
|
||||
}
|
||||
|
||||
if (!$privacy) {
|
||||
$errors[] = 'Необходимо согласие с условиями обработки персональных данных';
|
||||
}
|
||||
|
||||
// Проверяем существование email
|
||||
if (empty($errors) && $this->userModel->emailExists($email)) {
|
||||
$errors[] = 'Пользователь с таким email уже существует';
|
||||
}
|
||||
|
||||
if (!empty($errors)) {
|
||||
$_SESSION['registration_errors'] = $errors;
|
||||
$_SESSION['old_data'] = [
|
||||
'fio' => $fullName,
|
||||
'city' => $city,
|
||||
'email' => $email,
|
||||
'phone' => $phone
|
||||
];
|
||||
$this->redirect('/register');
|
||||
return;
|
||||
}
|
||||
|
||||
// Создаем пользователя
|
||||
try {
|
||||
$userId = $this->userModel->register([
|
||||
'email' => $email,
|
||||
'password' => $password,
|
||||
'full_name' => $fullName,
|
||||
'phone' => $phone,
|
||||
'city' => $city
|
||||
]);
|
||||
|
||||
if (!$userId) {
|
||||
throw new \Exception('Ошибка при создании пользователя');
|
||||
}
|
||||
|
||||
// Получаем созданного пользователя
|
||||
$user = $this->userModel->find($userId);
|
||||
|
||||
// Устанавливаем сессию
|
||||
$this->setSession($user);
|
||||
|
||||
$_SESSION['registration_success'] = 'Регистрация прошла успешно!';
|
||||
$this->redirect('/catalog');
|
||||
|
||||
} catch (\Exception $e) {
|
||||
$_SESSION['registration_errors'] = [$e->getMessage()];
|
||||
$_SESSION['old_data'] = [
|
||||
'fio' => $fullName,
|
||||
'city' => $city,
|
||||
'email' => $email,
|
||||
'phone' => $phone
|
||||
];
|
||||
$this->redirect('/register');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Выход из системы
|
||||
*/
|
||||
public function logout(): void
|
||||
{
|
||||
session_destroy();
|
||||
session_start();
|
||||
|
||||
$this->redirect('/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Установить сессию пользователя
|
||||
*/
|
||||
private function setSession(array $user): void
|
||||
{
|
||||
$_SESSION['user_id'] = $user['user_id'];
|
||||
$_SESSION['user_email'] = $user['email'];
|
||||
$_SESSION['full_name'] = $user['full_name'];
|
||||
$_SESSION['user_phone'] = $user['phone'] ?? '';
|
||||
$_SESSION['user_city'] = $user['city'] ?? '';
|
||||
$_SESSION['isLoggedIn'] = true;
|
||||
$_SESSION['isAdmin'] = (bool) $user['is_admin'];
|
||||
$_SESSION['login_time'] = time();
|
||||
}
|
||||
}
|
||||
|
||||
218
app/Controllers/CartController.php
Normal file
218
app/Controllers/CartController.php
Normal file
@@ -0,0 +1,218 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Core\Controller;
|
||||
use App\Models\Cart;
|
||||
use App\Models\Product;
|
||||
|
||||
/**
|
||||
* CartController - контроллер корзины
|
||||
*/
|
||||
class CartController extends Controller
|
||||
{
|
||||
private Cart $cartModel;
|
||||
private Product $productModel;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->cartModel = new Cart();
|
||||
$this->productModel = new Product();
|
||||
}
|
||||
|
||||
/**
|
||||
* Страница корзины
|
||||
*/
|
||||
public function index(): void
|
||||
{
|
||||
$this->requireAuth();
|
||||
|
||||
$user = $this->getCurrentUser();
|
||||
$cartItems = $this->cartModel->getUserCart($user['id']);
|
||||
$totals = $this->cartModel->getCartTotal($user['id']);
|
||||
|
||||
$this->view('cart/checkout', [
|
||||
'user' => $user,
|
||||
'cartItems' => $cartItems,
|
||||
'totalQuantity' => $totals['quantity'],
|
||||
'totalAmount' => $totals['amount']
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавить товар в корзину
|
||||
*/
|
||||
public function add(): void
|
||||
{
|
||||
if (!$this->isAuthenticated()) {
|
||||
$this->json([
|
||||
'success' => false,
|
||||
'message' => 'Требуется авторизация'
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
$productId = (int) $this->getPost('product_id', 0);
|
||||
$quantity = (int) $this->getPost('quantity', 1);
|
||||
$userId = $this->getCurrentUser()['id'];
|
||||
|
||||
if ($productId <= 0) {
|
||||
$this->json([
|
||||
'success' => false,
|
||||
'message' => 'Неверный товар'
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Проверяем наличие товара
|
||||
$product = $this->productModel->find($productId);
|
||||
|
||||
if (!$product || !$product['is_available']) {
|
||||
$this->json([
|
||||
'success' => false,
|
||||
'message' => 'Товар не найден'
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Проверяем количество на складе
|
||||
$cartItem = $this->cartModel->getItem($userId, $productId);
|
||||
$currentQty = $cartItem ? $cartItem['quantity'] : 0;
|
||||
$newQty = $currentQty + $quantity;
|
||||
|
||||
if ($newQty > $product['stock_quantity']) {
|
||||
$this->json([
|
||||
'success' => false,
|
||||
'message' => 'Недостаточно товара на складе'
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Добавляем в корзину
|
||||
$result = $this->cartModel->addItem($userId, $productId, $quantity);
|
||||
|
||||
if ($result) {
|
||||
$cartCount = $this->cartModel->getCount($userId);
|
||||
$this->json([
|
||||
'success' => true,
|
||||
'cart_count' => $cartCount,
|
||||
'message' => 'Товар добавлен в корзину'
|
||||
]);
|
||||
} else {
|
||||
$this->json([
|
||||
'success' => false,
|
||||
'message' => 'Ошибка при добавлении в корзину'
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Обновить количество товара
|
||||
*/
|
||||
public function update(): void
|
||||
{
|
||||
if (!$this->isAuthenticated()) {
|
||||
$this->json([
|
||||
'success' => false,
|
||||
'message' => 'Требуется авторизация'
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
$productId = (int) $this->getPost('product_id', 0);
|
||||
$quantity = (int) $this->getPost('quantity', 1);
|
||||
$userId = $this->getCurrentUser()['id'];
|
||||
|
||||
if ($quantity <= 0) {
|
||||
// Если количество 0 или меньше - удаляем
|
||||
$this->cartModel->removeItem($userId, $productId);
|
||||
$cartCount = $this->cartModel->getCount($userId);
|
||||
$this->json([
|
||||
'success' => true,
|
||||
'cart_count' => $cartCount
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Проверяем наличие на складе
|
||||
$product = $this->productModel->find($productId);
|
||||
if (!$product || $quantity > $product['stock_quantity']) {
|
||||
$this->json([
|
||||
'success' => false,
|
||||
'message' => 'Недостаточно товара на складе'
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
$result = $this->cartModel->updateQuantity($userId, $productId, $quantity);
|
||||
|
||||
if ($result) {
|
||||
$totals = $this->cartModel->getCartTotal($userId);
|
||||
$this->json([
|
||||
'success' => true,
|
||||
'cart_count' => $totals['quantity'],
|
||||
'total_amount' => $totals['amount']
|
||||
]);
|
||||
} else {
|
||||
$this->json([
|
||||
'success' => false,
|
||||
'message' => 'Ошибка при обновлении'
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Удалить товар из корзины
|
||||
*/
|
||||
public function remove(): void
|
||||
{
|
||||
if (!$this->isAuthenticated()) {
|
||||
$this->json([
|
||||
'success' => false,
|
||||
'message' => 'Требуется авторизация'
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
$productId = (int) $this->getPost('product_id', 0);
|
||||
$userId = $this->getCurrentUser()['id'];
|
||||
|
||||
$result = $this->cartModel->removeItem($userId, $productId);
|
||||
|
||||
if ($result) {
|
||||
$cartCount = $this->cartModel->getCount($userId);
|
||||
$this->json([
|
||||
'success' => true,
|
||||
'cart_count' => $cartCount
|
||||
]);
|
||||
} else {
|
||||
$this->json([
|
||||
'success' => false,
|
||||
'message' => 'Ошибка при удалении'
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить количество товаров в корзине
|
||||
*/
|
||||
public function count(): void
|
||||
{
|
||||
if (!$this->isAuthenticated()) {
|
||||
$this->json([
|
||||
'success' => true,
|
||||
'cart_count' => 0
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
$userId = $this->getCurrentUser()['id'];
|
||||
$count = $this->cartModel->getCount($userId);
|
||||
|
||||
$this->json([
|
||||
'success' => true,
|
||||
'cart_count' => $count
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
26
app/Controllers/HomeController.php
Normal file
26
app/Controllers/HomeController.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Core\Controller;
|
||||
|
||||
/**
|
||||
* HomeController - контроллер главной страницы
|
||||
*/
|
||||
class HomeController extends Controller
|
||||
{
|
||||
/**
|
||||
* Главная страница
|
||||
*/
|
||||
public function index(): void
|
||||
{
|
||||
$user = $this->getCurrentUser();
|
||||
|
||||
$this->view('home/index', [
|
||||
'user' => $user,
|
||||
'isLoggedIn' => $this->isAuthenticated(),
|
||||
'isAdmin' => $this->isAdmin()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
125
app/Controllers/OrderController.php
Normal file
125
app/Controllers/OrderController.php
Normal file
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Core\Controller;
|
||||
use App\Models\Order;
|
||||
use App\Models\Cart;
|
||||
|
||||
/**
|
||||
* OrderController - контроллер заказов
|
||||
*/
|
||||
class OrderController extends Controller
|
||||
{
|
||||
private Order $orderModel;
|
||||
private Cart $cartModel;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->orderModel = new Order();
|
||||
$this->cartModel = new Cart();
|
||||
}
|
||||
|
||||
/**
|
||||
* Страница оформления заказа (корзина)
|
||||
*/
|
||||
public function checkout(): void
|
||||
{
|
||||
$this->requireAuth();
|
||||
|
||||
$user = $this->getCurrentUser();
|
||||
$cartItems = $this->cartModel->getUserCart($user['id']);
|
||||
$totals = $this->cartModel->getCartTotal($user['id']);
|
||||
|
||||
$this->view('cart/checkout', [
|
||||
'user' => $user,
|
||||
'cartItems' => $cartItems,
|
||||
'totalQuantity' => $totals['quantity'],
|
||||
'totalAmount' => $totals['amount']
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Создание заказа
|
||||
*/
|
||||
public function create(): void
|
||||
{
|
||||
if (!$this->isAuthenticated()) {
|
||||
$this->json([
|
||||
'success' => false,
|
||||
'message' => 'Требуется авторизация'
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
$user = $this->getCurrentUser();
|
||||
$cartItems = $this->cartModel->getUserCart($user['id']);
|
||||
|
||||
if (empty($cartItems)) {
|
||||
$this->json([
|
||||
'success' => false,
|
||||
'message' => 'Корзина пуста'
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Получаем данные заказа
|
||||
$orderData = [
|
||||
'customer_name' => $this->getPost('full_name', $user['full_name']),
|
||||
'customer_email' => $this->getPost('email', $user['email']),
|
||||
'customer_phone' => $this->getPost('phone', $user['phone']),
|
||||
'delivery_address' => $this->getPost('address', ''),
|
||||
'delivery_region' => $this->getPost('region', ''),
|
||||
'postal_code' => $this->getPost('postal_code', ''),
|
||||
'delivery_method' => $this->getPost('delivery', 'courier'),
|
||||
'payment_method' => $this->getPost('payment', 'card'),
|
||||
'promo_code' => $this->getPost('promo_code', ''),
|
||||
'discount' => (float) $this->getPost('discount', 0),
|
||||
'delivery_price' => (float) $this->getPost('delivery_price', 2000),
|
||||
'notes' => $this->getPost('notes', '')
|
||||
];
|
||||
|
||||
// Валидация
|
||||
if (empty($orderData['customer_name'])) {
|
||||
$this->json([
|
||||
'success' => false,
|
||||
'message' => 'Укажите ФИО'
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($orderData['customer_phone'])) {
|
||||
$this->json([
|
||||
'success' => false,
|
||||
'message' => 'Укажите телефон'
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($orderData['delivery_address'])) {
|
||||
$this->json([
|
||||
'success' => false,
|
||||
'message' => 'Укажите адрес доставки'
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$result = $this->orderModel->createFromCart($user['id'], $cartItems, $orderData);
|
||||
|
||||
$this->json([
|
||||
'success' => true,
|
||||
'order_id' => $result['order_id'],
|
||||
'order_number' => $result['order_number'],
|
||||
'message' => 'Заказ успешно оформлен'
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
$this->json([
|
||||
'success' => false,
|
||||
'message' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
45
app/Controllers/PageController.php
Normal file
45
app/Controllers/PageController.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Core\Controller;
|
||||
|
||||
/**
|
||||
* PageController - контроллер статических страниц
|
||||
*/
|
||||
class PageController extends Controller
|
||||
{
|
||||
/**
|
||||
* Страница услуг
|
||||
*/
|
||||
public function services(): void
|
||||
{
|
||||
$this->view('pages/services', [
|
||||
'user' => $this->getCurrentUser(),
|
||||
'isLoggedIn' => $this->isAuthenticated()
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Страница доставки и оплаты
|
||||
*/
|
||||
public function delivery(): void
|
||||
{
|
||||
$this->view('pages/delivery', [
|
||||
'user' => $this->getCurrentUser(),
|
||||
'isLoggedIn' => $this->isAuthenticated()
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Страница гарантии
|
||||
*/
|
||||
public function warranty(): void
|
||||
{
|
||||
$this->view('pages/warranty', [
|
||||
'user' => $this->getCurrentUser(),
|
||||
'isLoggedIn' => $this->isAuthenticated()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
102
app/Controllers/ProductController.php
Normal file
102
app/Controllers/ProductController.php
Normal file
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Core\Controller;
|
||||
use App\Models\Product;
|
||||
use App\Models\Category;
|
||||
|
||||
/**
|
||||
* ProductController - контроллер товаров и каталога
|
||||
*/
|
||||
class ProductController extends Controller
|
||||
{
|
||||
private Product $productModel;
|
||||
private Category $categoryModel;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->productModel = new Product();
|
||||
$this->categoryModel = new Category();
|
||||
}
|
||||
|
||||
/**
|
||||
* Каталог товаров
|
||||
*/
|
||||
public function catalog(): void
|
||||
{
|
||||
$this->requireAuth();
|
||||
|
||||
$user = $this->getCurrentUser();
|
||||
$isAdmin = $this->isAdmin();
|
||||
|
||||
// Получаем параметры фильтрации
|
||||
$filters = [
|
||||
'category_id' => (int) $this->getQuery('category', 0),
|
||||
'search' => $this->getQuery('search', ''),
|
||||
'min_price' => (int) $this->getQuery('min_price', 0),
|
||||
'max_price' => (int) $this->getQuery('max_price', 1000000),
|
||||
'colors' => $this->getQuery('colors', []),
|
||||
'materials' => $this->getQuery('materials', [])
|
||||
];
|
||||
|
||||
$showAll = $isAdmin && $this->getQuery('show_all') === '1';
|
||||
|
||||
// Получаем данные
|
||||
$categories = $this->categoryModel->getActive();
|
||||
$products = $showAll
|
||||
? $this->productModel->getAllForAdmin(true)
|
||||
: $this->productModel->getAvailable($filters);
|
||||
|
||||
$availableColors = $this->productModel->getAvailableColors();
|
||||
$availableMaterials = $this->productModel->getAvailableMaterials();
|
||||
|
||||
// Подкатегории для выбранной категории
|
||||
$subcategories = [];
|
||||
if ($filters['category_id'] > 0) {
|
||||
$subcategories = $this->categoryModel->getChildren($filters['category_id']);
|
||||
}
|
||||
|
||||
$this->view('products/catalog', [
|
||||
'user' => $user,
|
||||
'isAdmin' => $isAdmin,
|
||||
'categories' => $categories,
|
||||
'subcategories' => $subcategories,
|
||||
'products' => $products,
|
||||
'filters' => $filters,
|
||||
'showAll' => $showAll,
|
||||
'availableColors' => $availableColors,
|
||||
'availableMaterials' => $availableMaterials,
|
||||
'success' => $this->getQuery('success'),
|
||||
'error' => $this->getQuery('error')
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Страница товара
|
||||
*/
|
||||
public function show(int $id): void
|
||||
{
|
||||
$this->requireAuth();
|
||||
|
||||
$product = $this->productModel->findWithCategory($id);
|
||||
|
||||
if (!$product || (!$product['is_available'] && !$this->isAdmin())) {
|
||||
$this->redirect('/catalog?error=product_not_found');
|
||||
return;
|
||||
}
|
||||
|
||||
$similarProducts = $this->productModel->getSimilar(
|
||||
$id,
|
||||
$product['category_id']
|
||||
);
|
||||
|
||||
$this->view('products/show', [
|
||||
'product' => $product,
|
||||
'similarProducts' => $similarProducts,
|
||||
'user' => $this->getCurrentUser(),
|
||||
'isAdmin' => $this->isAdmin()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user