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

PHP MySQL 購物車完整實作:Session + 資料庫混合架構

     編輯:PHP 相關知識

PHP MySQL 購物車系統完整原始碼與架構解析

購物車是電商系統核心模組。本文提供一個基於 Session 識別用戶 + MySQL 持久化資料 的完整購物車類別,支援加入、修改、刪除、清空、計算總價、列表顯示等完整功能,並附帶資料表結構與使用範例。

資料庫結構

CREATE TABLE `cart` (
  `id`        INT(11)     NOT NULL AUTO_INCREMENT,
  `session`   CHAR(32)    NOT NULL COMMENT '購物車 Session ID',
  `product`   VARCHAR(50) NOT NULL COMMENT '商品 ID 或 SKU',
  `quantity`  INT(11)     NOT NULL DEFAULT 1 COMMENT '數量',
  `created`   TIMESTAMP   NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_session_product` (`session`, `product`),
  KEY `idx_session` (`session`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE `products` (
  `id`          VARCHAR(50) NOT NULL PRIMARY KEY,
  `name`        VARCHAR(100) NOT NULL,
  `price`       DECIMAL(10,2) NOT NULL,
  `description` TEXT,
  `image`       VARCHAR(255)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

購物車核心類別

<?php
class Cart {
    private $db;
    private $table = 'cart';
    private $sessionId;

    public function __construct(PDO $db) {
        $this->db = $db;
        $this->initSession();
    }

    // 初始化 Session ID (Cookie 儲存,有效期 30 天)
    private function initSession() {
        if (isset($_COOKIE['cart_sid'])) {
            $this->sessionId = $_COOKIE['cart_sid'];
        } else {
            $this->sessionId = md5(uniqid(rand(), true));
            setcookie('cart_sid', $this->sessionId, time() + 2592000, '/'); // 30 天
        }
    }

    // 取得 Session ID (供前端顯示/除錯)
    public function getSessionId() {
        return $this->sessionId;
    }

    // 內部:檢查商品是否已在購物車
    private function checkItem($product) {
        $stmt = $this->db->prepare("SELECT quantity FROM {$this->table} WHERE session = ? AND product = ?");
        $stmt->execute([$this->sessionId, $product]);
        return $stmt->fetchColumn(); // 返回數量或 false
    }

    // 加入商品 / 增加數量
    public function addItem($product, $quantity = 1) {
        $qty = $this->checkItem($product);
        if ($qty === false) {
            $stmt = $this->db->prepare("INSERT INTO {$this->table} (session, product, quantity) VALUES (?, ?, ?)");
            $stmt->execute([$this->sessionId, $product, $quantity]);
        } else {
            $newQty = $qty + $quantity;
            $stmt = $this->db->prepare("UPDATE {$this->table} SET quantity = ? WHERE session = ? AND product = ?");
            $stmt->execute([$newQty, $this->sessionId, $product]);
        }
        return true;
    }

    // 移除商品
    public function deleteItem($product) {
        $stmt = $this->db->prepare("DELETE FROM {$this->table} WHERE session = ? AND product = ?");
        return $stmt->execute([$this->sessionId, $product]);
    }

    // 修改商品數量
    public function modifyQuantity($product, $quantity) {
        if ($quantity <= 0) return $this->deleteItem($product);
        $stmt = $this->db->prepare("UPDATE {$this->table} SET quantity = ? WHERE session = ? AND product = ?");
        return $stmt->execute([$quantity, $this->sessionId, $product]);
    }

    // 清空購物車
    public function clearCart() {
        $stmt = $this->db->prepare("DELETE FROM {$this->table} WHERE session = ?");
        return $stmt->execute([$this->sessionId]);
    }

    // 計算總金額
    public function getTotal() {
        $stmt = $this->db->prepare("
            SELECT SUM(p.price * c.quantity) AS total
            FROM {$this->table} c
            JOIN products p ON c.product = p.id
            WHERE c.session = ?
        ");
        $stmt->execute([$this->sessionId]);
        return (float)$stmt->fetchColumn();
    }

    // 取得購物車詳細內容 (含商品資訊)
    public function getContents() {
        $stmt = $this->db->prepare("
            SELECT c.product, c.quantity, p.name, p.price, p.description, p.image,
                   (p.price * c.quantity) AS subtotal
            FROM {$this->table} c
            JOIN products p ON c.product = p.id
            WHERE c.session = ?
            ORDER BY c.id
        ");
        $stmt->execute([$this->sessionId]);
        $items = $stmt->fetchAll(PDO::FETCH_ASSOC);
        
        $total = 0;
        foreach ($items as &$item) {
            $item['price'] = (float)$item['price'];
            $item['subtotal'] = (float)$item['subtotal'];
            $total += $item['subtotal'];
        }
        return ['items' => $items, 'total' => $total, 'count' => count($items)];
    }

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

    // 取得商品總數量
    public function getTotalQuantity() {
        $stmt = $this->db->prepare("SELECT SUM(quantity) FROM {$this->table} WHERE session = ?");
        $stmt->execute([$this->sessionId]);
        return (int)$stmt->fetchColumn();
    }
}
?>

使用範例:加入商品流程

<?php
require_once 'db.php'; // PDO 連線
require_once 'Cart.php';

$cart = new Cart($pdo);

// 加入商品 (從 POST 取得)
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['product_id'])) {
    $productId = $_POST['product_id'];
    $qty = max(1, (int)($_POST['quantity'] ?? 1));
    
    try {
        $cart->addItem($productId, $qty);
        // AJAX 回應
        echo json_encode(['success' => true, 'count' => $cart->getTotalQuantity()]);
    } catch (Exception $e) {
        echo json_encode(['success' => false, 'message' => $e->getMessage()]);
    }
    exit;
}
?>

前端顯示購物車內容

<?php
$cartData = $cart->getContents();
?>
<table class="cart-table">
    <thead>
        <tr><th>商品</th><th>單價</th><th>數量</th><th>小計</th><th>操作</th></tr>
    </thead>
    <tbody>
    <?php foreach ($cartData['items'] as $item): ?>
        <tr data-product="<?php echo $item['product']; ?>">
            <td><img src="<?php echo $item['image']; ?>" width="50"> <?php echo htmlspecialchars($item['name']); ?></td>
            <td>¥<?php echo number_format($item['price'], 2); ?></td>
            <td>
                <input type="number" class="qty-input" value="<?php echo $item['quantity']; ?>" min="1" max="99">
            </td>
            <td>¥<?php echo number_format($item['subtotal'], 2); ?></td>
            <td><button class="btn-remove">移除</button></td>
        </tr>
    <?php endforeach; ?>
    </tbody>
    <tfoot>
        <tr><td colspan="3">總計</td><td>¥<?php echo number_format($cartData['total'], 2); ?></td><td></td></tr>
    </tfoot>
</table>

架構優勢與擴充建議

特性說明
Session + DB 混合未登入用戶靠 Cookie 識別,登入後可將 Session ID 關聯到用戶帳號,實現跨裝置同步。
唯一索引防重複uk_session_product 確保同一購物車不會有重複商品行。
PDO 預處理全程使用參數綁定,防止 SQL 注入。
擴充欄位可在 cart 表加入 attrs (JSON) 儲存規格 (顏色/尺寸)、price_override 處理促銷價。
快取優化高流量下可將購物車內容寫入 Redis (Key: cart:{sessionId}),降低 DB 壓力。

相關資源連結

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved