- Создано ядро 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
218 lines
6.7 KiB
PHP
218 lines
6.7 KiB
PHP
<?php
|
||
|
||
namespace App\Models;
|
||
|
||
use App\Core\Model;
|
||
use App\Core\Database;
|
||
|
||
/**
|
||
* Order - модель заказа
|
||
*/
|
||
class Order extends Model
|
||
{
|
||
protected string $table = 'orders';
|
||
protected string $primaryKey = 'order_id';
|
||
|
||
/**
|
||
* Создать заказ из корзины
|
||
*/
|
||
public function createFromCart(int $userId, array $cartItems, array $orderData): ?array
|
||
{
|
||
$db = Database::getInstance();
|
||
|
||
try {
|
||
$db->beginTransaction();
|
||
|
||
// Считаем итоговую сумму
|
||
$subtotal = 0;
|
||
foreach ($cartItems as $item) {
|
||
$subtotal += $item['price'] * $item['quantity'];
|
||
}
|
||
|
||
$discountAmount = (float) ($orderData['discount'] ?? 0);
|
||
$deliveryPrice = (float) ($orderData['delivery_price'] ?? 2000);
|
||
$finalAmount = $subtotal - $discountAmount + $deliveryPrice;
|
||
|
||
// Генерируем номер заказа
|
||
$orderNumber = 'ORD-' . date('Ymd-His') . '-' . rand(1000, 9999);
|
||
|
||
// Создаем заказ
|
||
$orderId = $this->create([
|
||
'user_id' => $userId,
|
||
'order_number' => $orderNumber,
|
||
'customer_name' => $orderData['customer_name'],
|
||
'customer_email' => $orderData['customer_email'],
|
||
'customer_phone' => $orderData['customer_phone'],
|
||
'delivery_address' => $orderData['delivery_address'],
|
||
'delivery_region' => $orderData['delivery_region'] ?? null,
|
||
'postal_code' => $orderData['postal_code'] ?? null,
|
||
'delivery_method' => $orderData['delivery_method'] ?? 'courier',
|
||
'payment_method' => $orderData['payment_method'] ?? 'card',
|
||
'subtotal' => $subtotal,
|
||
'discount_amount' => $discountAmount,
|
||
'delivery_price' => $deliveryPrice,
|
||
'final_amount' => $finalAmount,
|
||
'promo_code' => $orderData['promo_code'] ?? null,
|
||
'status' => 'pending',
|
||
'notes' => $orderData['notes'] ?? null
|
||
]);
|
||
|
||
if (!$orderId) {
|
||
throw new \Exception('Не удалось создать заказ');
|
||
}
|
||
|
||
// Добавляем товары в заказ
|
||
foreach ($cartItems as $item) {
|
||
$this->addOrderItem($orderId, $item);
|
||
}
|
||
|
||
// Уменьшаем количество товаров на складе
|
||
$productModel = new Product();
|
||
foreach ($cartItems as $item) {
|
||
$productModel->decreaseStock($item['product_id'], $item['quantity']);
|
||
}
|
||
|
||
// Очищаем корзину
|
||
$cartModel = new Cart();
|
||
$cartModel->clearCart($userId);
|
||
|
||
$db->commit();
|
||
|
||
return [
|
||
'order_id' => $orderId,
|
||
'order_number' => $orderNumber,
|
||
'final_amount' => $finalAmount
|
||
];
|
||
|
||
} catch (\Exception $e) {
|
||
$db->rollBack();
|
||
throw $e;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Добавить товар в заказ
|
||
*/
|
||
private function addOrderItem(int $orderId, array $item): void
|
||
{
|
||
$sql = "INSERT INTO order_items
|
||
(order_id, product_id, product_name, quantity, product_price, total_price)
|
||
VALUES (?, ?, ?, ?, ?, ?)";
|
||
|
||
$totalPrice = $item['price'] * $item['quantity'];
|
||
|
||
$this->execute($sql, [
|
||
$orderId,
|
||
$item['product_id'],
|
||
$item['name'],
|
||
$item['quantity'],
|
||
$item['price'],
|
||
$totalPrice
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* Получить заказ с подробностями
|
||
*/
|
||
public function getWithDetails(int $orderId): ?array
|
||
{
|
||
$sql = "SELECT o.*, u.email as user_email, u.full_name as user_full_name
|
||
FROM {$this->table} o
|
||
LEFT JOIN users u ON o.user_id = u.user_id
|
||
WHERE o.order_id = ?";
|
||
|
||
$order = $this->queryOne($sql, [$orderId]);
|
||
|
||
if ($order) {
|
||
$order['items'] = $this->getOrderItems($orderId);
|
||
}
|
||
|
||
return $order;
|
||
}
|
||
|
||
/**
|
||
* Получить товары заказа
|
||
*/
|
||
public function getOrderItems(int $orderId): array
|
||
{
|
||
$sql = "SELECT oi.*, p.image_url
|
||
FROM order_items oi
|
||
LEFT JOIN products p ON oi.product_id = p.product_id
|
||
WHERE oi.order_id = ?";
|
||
return $this->query($sql, [$orderId]);
|
||
}
|
||
|
||
/**
|
||
* Получить заказы пользователя
|
||
*/
|
||
public function getUserOrders(int $userId, int $limit = 50): array
|
||
{
|
||
$sql = "SELECT * FROM {$this->table}
|
||
WHERE user_id = ?
|
||
ORDER BY created_at DESC
|
||
LIMIT ?";
|
||
return $this->query($sql, [$userId, $limit]);
|
||
}
|
||
|
||
/**
|
||
* Получить все заказы для админки
|
||
*/
|
||
public function getAllForAdmin(int $limit = 50): array
|
||
{
|
||
$sql = "SELECT o.*, u.email as user_email
|
||
FROM {$this->table} o
|
||
LEFT JOIN users u ON o.user_id = u.user_id
|
||
ORDER BY o.created_at DESC
|
||
LIMIT ?";
|
||
return $this->query($sql, [$limit]);
|
||
}
|
||
|
||
/**
|
||
* Обновить статус заказа
|
||
*/
|
||
public function updateStatus(int $orderId, string $status): bool
|
||
{
|
||
$updateData = [
|
||
'status' => $status,
|
||
'updated_at' => date('Y-m-d H:i:s')
|
||
];
|
||
|
||
if ($status === 'completed') {
|
||
$updateData['completed_at'] = date('Y-m-d H:i:s');
|
||
}
|
||
|
||
return $this->update($orderId, $updateData);
|
||
}
|
||
|
||
/**
|
||
* Получить статистику заказов
|
||
*/
|
||
public function getStats(): array
|
||
{
|
||
$stats = [];
|
||
|
||
// Общее количество заказов
|
||
$result = $this->queryOne("SELECT COUNT(*) as cnt FROM {$this->table}");
|
||
$stats['total'] = (int) $result['cnt'];
|
||
|
||
// Выручка
|
||
$result = $this->queryOne(
|
||
"SELECT COALESCE(SUM(final_amount), 0) as revenue
|
||
FROM {$this->table} WHERE status = 'completed'"
|
||
);
|
||
$stats['revenue'] = (float) $result['revenue'];
|
||
|
||
// По статусам
|
||
$statuses = $this->query(
|
||
"SELECT status, COUNT(*) as cnt FROM {$this->table} GROUP BY status"
|
||
);
|
||
$stats['by_status'] = [];
|
||
foreach ($statuses as $s) {
|
||
$stats['by_status'][$s['status']] = (int) $s['cnt'];
|
||
}
|
||
|
||
return $stats;
|
||
}
|
||
}
|
||
|