Hướng dẫn Code chức năng giỏ hàng PHP MySQL từ A đến Z: Kỹ thuật lập trình chuẩn & Tối ưu

Chào các bạn, tôi là Full-stack Web Developer. Trong thế giới E-commerce, giỏ hàng (Shopping Cart) không chỉ là một mảng dữ liệu lưu trữ sản phẩm; nó là “trái tim” quyết định tỷ lệ chuyển đổi. Code chạy được thì dễ, nhưng code để hệ thống chịu tải tốt, bảo mật và dễ bảo trì mới là thử thách.

Hôm nay, tôi sẽ hướng dẫn bạn xây dựng chức năng giỏ hàng theo phong cách Clean Code, tách biệt Logic và View.


Vấn đề thực tế: Tại sao Developer cần quan tâm đến Code chức năng giỏ hàng PHP MySQL từ A đến Z?

Nhiều bạn mới thường viết code giỏ hàng bằng cách nhồi nhét $_SESSION trực tiếp vào các file HTML. Hậu quả là:

  1. Spaghetti Code: Logic tính toán thuế, giảm giá, phí ship lẫn lộn với code giao diện.
  2. Sai lệch dữ liệu: Khách hàng thay đổi giá sản phẩm trong lúc thanh toán nếu bạn lưu giá trực tiếp vào Session thay vì mapping với Database.
  3. Trải nghiệm tệ: Reload trang mỗi khi thêm hàng làm giảm 20-30% tỷ lệ mua hàng.

Giải pháp kỹ thuật:

1. Phân tích luồng dữ liệu (Data Flow)

Chúng ta sẽ sử dụng kết hợp Session (để lưu trữ tạm thời) và MySQL (để lấy thông tin sản phẩm thực tế).

  • Step 1: User nhấn “Thêm vào giỏ”.
  • Step 2: Gửi Request (ID, Quantity) lên Server thông qua AJAX/Controller.
  • Step 3: Server kiểm tra tồn kho, lấy giá mới nhất từ DB.
  • Step 4: Cập nhật vào đối tượng Cart.
  • Step 5: Trả về JSON để cập nhật UI mà không cần load lại trang.

2. Hướng dẫn Code từng phần

Bước 1: Khởi tạo Database mẫu

CREATE TABLE `products` (
  `id` INT PRIMARY KEY AUTO_INCREMENT,
  `name` VARCHAR(255),
  `price` DECIMAL(10,2),
  `image` VARCHAR(255),
  `stock` INT
);

Bước 2: Xây dựng Lớp Giỏ Hàng (Cart Service)

Thay vì thao tác trực tiếp với $_SESSION, ta đóng gói nó vào một Class để dễ quản lý.


Code mẫu hoàn chỉnh (Best Practice)

1. Cart.php (Logic xử lý)

Tôi sử dụng Singleton Pattern hoặc một Class đơn giản để quản lý.

<?php
session_start();

class Cart {
    public function __construct() {
        if (!isset($_SESSION['cart'])) {
            $_SESSION['cart'] = [];
        }
    }

    public function add($product, $quantity = 1) {
        $id = $product['id'];
        if (isset($_SESSION['cart'][$id])) {
            $_SESSION['cart'][$id]['quantity'] += $quantity;
        } else {
            $_SESSION['cart'][$id] = [
                'id' => $id,
                'name' => $product['name'],
                'price' => $product['price'],
                'image' => $product['image'],
                'quantity' => $quantity
            ];
        }
    }

    public function update($id, $quantity) {
        if (isset($_SESSION['cart'][$id])) {
            $_SESSION['cart'][$id]['quantity'] = $quantity;
            if ($_SESSION['cart'][$id]['quantity'] <= 0) {
                $this->remove($id);
            }
        }
    }

    public function remove($id) {
        unset($_SESSION['cart'][$id]);
    }

    public function getTotalContent() {
        return $_SESSION['cart'];
    }

    public function getTotalPrice() {
        $total = 0;
        foreach ($_SESSION['cart'] as $item) {
            $total += $item['price'] * $item['quantity'];
        }
        return $total;
    }
}

2. CartController.php (Điều hướng)

Xử lý Request từ Client. Lưu ý sử dụng PDO để chống SQL Injection.

<?php
require_once 'Cart.php';
require_once 'Database.php'; // Giả định bạn đã có class kết nối DB

$cart = new Cart();
$action = $_GET['action'] ?? '';

if ($action === 'add') {
    $id = (int)$_POST['id'];
    // Sử dụng Prepared Statement để bảo mật
    $stmt = $pdo->prepare("SELECT * FROM products WHERE id = ?");
    $stmt->execute([$id]);
    $product = $stmt->fetch();

    if ($product) {
        $cart->add($product);
        echo json_encode(['status' => 'success', 'total_items' => count($cart->getTotalContent())]);
    }
}

3. Frontend (Xử lý AJAX với JavaScript)

Để website chuyên nghiệp, ta không nên load lại trang.

function addToCart(productId) {
    const formData = new FormData();
    formData.append('id', productId);

    fetch('CartController.php?action=add', {
        method: 'POST',
        body: formData
    })
    .then(response => response.json())
    .then(data => {
        if(data.status === 'success') {
            document.getElementById('cart-count').innerText = data.total_items;
            alert('Đã thêm sản phẩm vào giỏ hàng!');
        }
    })
    .catch(error => console.error('Error:', error));
}

Lưu ý về Bảo mật & Hiệu năng

1. Bảo mật

  • SQL Injection: Luôn sử dụng PDO hoặc MySQLi với Prepared Statements khi lấy thông tin sản phẩm để thêm vào giỏ. Tuyệt đối không tin tưởng id gửi lên từ Client.
  • XSS (Cross-Site Scripting): Khi hiển thị tên sản phẩm từ giỏ hàng ra View, hãy dùng htmlspecialchars() để tránh bị chèn script độc hại.
  • CSRF: Trong các hệ thống lớn (như Laravel), mỗi request “Add to cart” cần kèm theo một CSRF Token để tránh việc User bị ép thêm hàng vào giỏ ngoài ý muốn.

2. Hiệu năng

  • Tránh N+1 Query: Khi hiển thị trang giỏ hàng, đừng thực hiện câu Query trong vòng lặp foreach của Session. Hãy lấy danh sách ID, thực hiện 1 câu WHERE IN (...) để lấy dữ liệu sản phẩm mới nhất một lần duy nhất.
  • Database Indexing: Đảm bảo cột id trong bảng products được đánh Index (mặc định là Primary key).
  • Cache: Nếu sản phẩm ít thay đổi, có thể dùng Redis để lưu trữ thông tin sản phẩm thay vì query MySQL liên tục.

Kết luận

Xây dựng chức năng giỏ hàng không khó, nhưng để xây dựng đúng chuẩn chuyên nghiệp đòi hỏi tư duy phân tách logic rõ ràng. Việc sử dụng Class quản lý giỏ hàng giúp code của bạn dễ Unit Test, dễ mở rộng (như thêm chức năng Coupon, Flash Sale) mà không làm hỏng cấu trúc cũ.

Hãy luôn nhớ: “Code cho máy hiểu thì ai cũng làm được, nhưng code cho đồng nghiệp hiểu và hệ thống chạy nhanh mới là Developer giỏi.”

Chúc các bạn thành công! Nếu có thắc mắc về Design Pattern trong PHP, hãy để lại bình luận nhé.

See more: Code chức năng giỏ hàng PHP MySQL từ A đến Z.

Discover: Python Trick.

By admin

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *