首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 購物車核心功能實現:更新數量、刪除、清空與總價計算的完整類封裝

     編輯:PHP 相關知識

購物車數據模型

CREATE TABLE `cart` (
    `id` INT AUTO_INCREMENT PRIMARY KEY,
    `session_id` VARCHAR(64) NOT NULL,      -- 關聯用戶會話
    `product_id` INT NOT NULL,              -- 商品 ID
    `quantity` INT UNSIGNED NOT NULL DEFAULT 1,
    `added_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    UNIQUE KEY `uk_session_product` (`session_id`, `product_id`),
    INDEX `idx_session` (`session_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

現代 PDO 版購物車類

<?php
class Cart {
    private PDO $pdo;
    private string $sessionId;
    private string $table = 'cart';

    public function __construct(PDO $pdo, string $sessionId) {
        $this->pdo = $pdo;
        $this->sessionId = $sessionId;
    }

    /** 獲取購物車當前項目 */
    public function getItems(): array {
        $stmt = $this->pdo->prepare("
            SELECT c.*, p.name, p.price, p.image, p.stock
            FROM {$this->table} c
            JOIN products p ON c.product_id = p.id
            WHERE c.session_id = ?
            ORDER BY c.added_at
        ");
        $stmt->execute([$this->sessionId]);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    /** 添加商品(存在則增加數量) */
    public function add(int $productId, int $quantity = 1): bool {
        // 檢查庫存
        $stmt = $this->pdo->prepare("SELECT stock FROM products WHERE id = ?");
        $stmt->execute([$productId]);
        $stock = $stmt->fetchColumn();
        if ($stock === false || $stock < $quantity) return false;

        $this->pdo->beginTransaction();
        try {
            // 嘗試更新
            $stmt = $this->pdo->prepare("
                UPDATE {$this->table} SET quantity = quantity + ? 
                WHERE session_id = ? AND product_id = ?
            ");
            $stmt->execute([$quantity, $this->sessionId, $productId]);

            if ($stmt->rowCount() === 0) {
                // 不存在則插入
                $stmt = $this->pdo->prepare("
                    INSERT INTO {$this->table} (session_id, product_id, quantity) VALUES (?, ?, ?)
                ");
                $stmt->execute([$this->sessionId, $productId, $quantity]);
            }
            $this->pdo->commit();
            return true;
        } catch (Exception $e) {
            $this->pdo->rollBack();
            return false;
        }
    }

    /** 更新數量(0 表示刪除) */
    public function updateQuantity(int $productId, int $quantity): bool {
        if ($quantity <= 0) return $this->remove($productId);

        // 檢查庫存
        $stmt = $this->pdo->prepare("SELECT stock FROM products WHERE id = ?");
        $stmt->execute([$productId]);
        if ($stmt->fetchColumn() < $quantity) return false;

        $stmt = $this->pdo->prepare("
            UPDATE {$this->table} SET quantity = ? 
            WHERE session_id = ? AND product_id = ?
        ");
        return $stmt->execute([$quantity, $this->sessionId, $productId]);
    }

    /** 刪除單項 */
    public function remove(int $productId): bool {
        $stmt = $this->pdo->prepare("DELETE FROM {$this->table} WHERE session_id = ? AND product_id = ?");
        return $stmt->execute([$this->sessionId, $productId]);
    }

    /** 清空購物車 */
    public function clear(): bool {
        $stmt = $this->pdo->prepare("DELETE FROM {$this->table} WHERE session_id = ?");
        return $stmt->execute([$this->sessionId]);
    }

    /** 獲取總價 */
    public function getTotal(): float {
        $stmt = $this->pdo->prepare("
            SELECT SUM(c.quantity * p.price) as total
            FROM {$this->table} c
            JOIN products p ON c.product_id = p.id
            WHERE c.session_id = ?
        ");
        $stmt->execute([$this->sessionId]);
        return (float)($stmt->fetchColumn() ?: 0);
    }

    /** 獲取商品種類數 */
    public function getItemCount(): int {
        $stmt = $this->pdo->prepare("SELECT COUNT(*) FROM {$this->table} WHERE session_id = ?");
        $stmt->execute([$this->sessionId]);
        return (int)$stmt->fetchColumn();
    }

    /** 獲取商品總件數 */
    public function getTotalQuantity(): int {
        $stmt = $this->pdo->prepare("SELECT SUM(quantity) FROM {$this->table} WHERE session_id = ?");
        $stmt->execute([$this->sessionId]);
        return (int)($stmt->fetchColumn() ?: 0);
    }
}

控制器使用示例

<?php
// cart.php
session_start();
require_once 'Cart.php';
require_once 'pdo_config.php';

$cart = new Cart($pdo, session_id());

$action = $_GET['action'] ?? 'view';

switch ($action) {
    case 'add':
        $pid = (int)($_POST['product_id'] ?? 0);
        $qty = (int)($_POST['quantity'] ?? 1);
        if ($pid && $cart->add($pid, $qty)) {
            header('Location: cart.php?msg=added');
        } else {
            header('Location: cart.php?err=stock');
        }
        exit;

    case 'update':
        $pid = (int)($_POST['product_id'] ?? 0);
        $qty = (int)($_POST['quantity'] ?? 0);
        if ($pid && $cart->updateQuantity($pid, $qty)) {
            header('Location: cart.php?msg=updated');
        } else {
            header('Location: cart.php?err=update');
        }
        exit;

    case 'remove':
        $pid = (int)($_GET['product_id'] ?? 0);
        if ($pid && $cart->remove($pid)) {
            header('Location: cart.php?msg=removed');
        }
        exit;

    case 'clear':
        $cart->clear();
        header('Location: cart.php?msg=cleared');
        exit;

    default: // view
        $items = $cart->getItems();
        $total = $cart->getTotal();
        include 'cart_view.php';
}

視圖模板 (cart_view.php)

<form method="post" action="cart.php?action=update">
<table class="cart-table">
    <thead>
        <tr><th>商品</th><th>單價</th><th>數量</th><th>小計</th><th>操作</th></tr>
    </thead>
    <tbody>
    <?php foreach ($items as $item): ?>
        <tr>
            <td><img src="<?= $item['image'] ?>" width="50"> <?= htmlspecialchars($item['name']) ?></td>
            <td><?= number_format($item['price'], 2) ?></td>
            <td>
                <input type="number" name="quantity[<?= $item['product_id'] ?>]" 
                       value="<?= $item['quantity'] ?>" min="0" max="<?= $item['stock'] ?>" style="width:60px">
            </td>
            <td><?= number_format($item['price'] * $item['quantity'], 2) ?></td>
            <td>
                <a href="cart.php?action=remove&product_id=<?= $item['product_id'] ?>" 
                   onclick="return confirm('確定刪除?')>刪除</a>
            </td>
        </tr>
    <?php endforeach; ?>
    </tbody>
</table>
<div class="cart-actions">
    <button type="submit">更新購物車</button>
    <a href="cart.php?action=clear" onclick="return confirm('清空購物車?')>清空</a>
    <a href="checkout.php">去結算</a>
</div>
</form>
<div class="cart-summary">總計:<strong><?= number_format($total, 2) ?></strong> 元</div>

Session 型購物車(無需數據庫,適合簡單場景)

<?php
class SessionCart {
    private const KEY = 'cart_items';

    public static function get(): array {
        return $_SESSION[self::KEY] ?? [];
    }

    public static function add(int $productId, int $qty = 1): void {
        $cart = self::get();
        $cart[$productId] = ($cart[$productId] ?? 0) + $qty;
        $_SESSION[self::KEY] = $cart;
    }

    public static function update(int $productId, int $qty): void {
        $cart = self::get();
        if ($qty <= 0) unset($cart[$productId]);
        else $cart[$productId] = $qty;
        $_SESSION[self::KEY] = $cart;
    }

    public static function remove(int $productId): void {
        $cart = self::get();
        unset($cart[$productId]);
        $_SESSION[self::KEY] = $cart;
    }

    public static function clear(): void {
        unset($_SESSION[self::KEY]);
    }

    public static function getTotal(PDO $pdo): float {
        $cart = self::get();
        if (!$cart) return 0;
        $ids = implode(',', array_keys($cart));
        $stmt = $pdo->query("SELECT id, price FROM products WHERE id IN ($ids)");
        $prices = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
        $total = 0;
        foreach ($cart as $pid => $qty) {
            $total += ($prices[$pid] ?? 0) * $qty;
        }
        return $total;
    }
}

選型建議

場景推薦方案
用戶登錄制、多端同步、持久化需求數據庫型(上方 Cart 類)
遊客瀏覽、單會話、簡單商品Session 型
高併發、分佈式Redis 哈希結構 HSET cart:{session} {pid} {qty}
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved