Skip to content

Instantly share code, notes, and snippets.

@tuannguyen29
Created May 5, 2025 10:42
Show Gist options
  • Save tuannguyen29/c88d7fcdf7e42c7e03b1d2cb29e2db5d to your computer and use it in GitHub Desktop.
Save tuannguyen29/c88d7fcdf7e42c7e03b1d2cb29e2db5d to your computer and use it in GitHub Desktop.

Hướng dẫn lập trình dự án PHP MVC thuần với MySQL và Bootstrap 5

Bài lab yêu cầu xây dựng một ứng dụng web theo mô hình MVC (Model-View-Controller) bằng PHP thuần, sử dụng MySQL để lưu trữ dữ liệu và Bootstrap 5 cho giao diện. Các chức năng chính bao gồm phân hệ Admin (quản lý user, sản phẩm, đăng nhập) và phân hệ Frontend (danh sách sản phẩm, đăng nhập thành viên, xem đơn hàng, cập nhật thông tin cá nhân). Dự án có thể chạy trên localhost (ví dụ dùng XAMPP/WAMP/MAMP).

Theo mô hình MVC, mã nguồn được chia thành ba phần riêng biệt: Model (xử lý dữ liệu, giao tiếp CSDL), View (giao diện hiển thị) và Controller (điều khiển luồng dữ liệu giữa Model và View). MVC giúp “tổ chức code theo từng phần độc lập” để dễ quản lý và bảo trì. Ví dụ, cấu trúc thư mục dự án MVC có thể như sau (tham khảo):

project_root/
  assets/             # file tĩnh: CSS, JS, hình ảnh, fonts
  controllers/        # các lớp Controller
  models/             # các lớp Model (giao tiếp CSDL)
  views/              # giao diện (HTML/PHP templates)
    layouts/          # các layout/chung (vd. main_layout.php)
    admin/            # view cho phần Admin (thêm, sửa user, sản phẩm)
    member/           # view cho phần Member (đăng nhập, profile, đơn hàng)
  config/             # (nếu có) file cấu hình, kết nối DB
  index.php           # điểm vào (Front Controller) điều hướng request
  routes.php          # (nếu dùng) định nghĩa đường dẫn, controller/action

Trong đó, mỗi thư mục có vai trò: controllers/ chứa các file Controller (xử lý request, gọi Model và trả về View), models/ chứa các file định nghĩa lớp Model (các đối tượng dữ liệu và các phương thức thao tác CSDL), views/ chứa các file giao diện (sử dụng Bootstrap 5 để tạo layout responsive). Ví dụ một file connection.php hoặc config/DB.php sẽ dùng PDO để kết nối MySQL; một file index.php sẽ nhận mọi yêu cầu và chuyển đến đúng Controller/Action tương ứng.

Môi trường và cài đặt

  • Yêu cầu môi trường: PHP (>=7.4 hoặc PHP 8), MySQL (hoặc MariaDB), máy chủ web Apache (có thể dùng XAMPP/WAMP/MAMP). Bật extension PDO nếu dùng PDO.

  • Cài đặt XAMPP (ví dụ): Cài đặt XAMPP và start ApacheMySQL. Tất cả file PHP nên đặt trong thư mục htdocs của XAMPP (ví dụ C:\xampp\htdocs\ten_project). Khi chạy, gõ địa chỉ http://localhost/ten_project/ trên trình duyệt.

  • Cơ sở dữ liệu: Tạo database (ví dụ lab_db) trên MySQL. Tạo các bảng cần thiết: bảng users (có cột id, name, email, password, role, …), bảng products (id, name, price, description, …), và bảng orders hoặc tương tự để lưu sản phẩm đã mua của từng user (ví dụ bảng orders với user_id, product_id, quantity,…). Có thể chuẩn bị file SQL mẫu và hướng dẫn import qua phpMyAdmin.

  • Cấu hình kết nối DB: Ví dụ tạo file config/DB.php hoặc connection.php chứa class kết nối đến database. Sử dụng PDO để kết nối và xử lý CSDL. Ví dụ :

    <?php
    class DB {
        private static $instance = null;
        public static function getInstance() {
            if (!isset(self::$instance)) {
                try {
                    self::$instance = new PDO(
                      'mysql:host=localhost;dbname=lab_db;charset=utf8', 
                      'root', 
                      ''
                    );
                    self::$instance->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
                } catch (PDOException $ex) {
                    die("Kết nối CSDL thất bại: " . $ex->getMessage());
                }
            }
            return self::$instance;
        }
    }
    ?>

    Chú ý cập nhật tên host, database, username và password cho phù hợp (ví dụ thay lab_db, root, '').

  • Khởi chạy dự án: Sau khi copy mã nguồn vào thư mục localhost, mở trình duyệt nhập http://localhost/ten_project/ (hoặc thêm index.php). Nếu dùng hệ thống routing với .htaccess, cần bật mod_rewrite. Thường thì file index.php sẽ xử lý tham số controlleraction từ URL để gọi Controller tương ứng.

Cấu trúc thư mục chi tiết

Ví dụ một cấu trúc (tham khảo trên Viblo):

  • assets/: chứa CSS (ví dụ file Bootstrap), JavaScript (có thể dùng jQuery, Bootstrap JS), hình ảnh, phông chữ...

  • config/ hoặc connection.php: file thiết lập kết nối DB (class DB/PDO như trên).

  • controllers/: các file Controller, ví dụ AdminController.php, UserController.php, ProductController.php, AuthController.php, HomeController.php… Mỗi Controller có các phương thức xử lý tương ứng (action) như:

    • AdminController: đăng nhập admin (login()), đăng xuất (logout()).
    • UserController (trong admin): index() (hiển thị danh sách users), create(), store(), edit($id), update($id), delete($id) – thực hiện CRUD user.
    • ProductController (trong admin): tương tự index(), create(), store(), edit(), update(), delete() cho sản phẩm.
    • HomeController (frontend): productList() hiển thị danh sách sản phẩm, hỗ trợ tìm kiếm/pagination.
    • AuthController (frontend): loginMember(), logoutMember().
    • ProfileController: viewProfile(), updateProfile(), myOrders() (danh sách đơn hàng / sản phẩm đã mua của member).
  • models/: các file Model, mỗi file định nghĩa một lớp đại diện dữ liệu và phương thức tương tác CSDL. Ví dụ User.php chứa class User với thuộc tính tương ứng trường bảng users (id, name, email, password, role,…) và các phương thức như all(), find($id), save(), update(), delete(). Tương tự Product.php cho bảng products, và nếu có bảng orders thì tạo Order.php. Mỗi Model sẽ dùng PDO (DB::getInstance()) để thực hiện truy vấn INSERT, SELECT, UPDATE, DELETE.

  • views/: chứa các file giao diện (thường là file .php có HTML kết hợp PHP). Chia thành các thư mục con cho từng khu vực:

    • views/layouts/: layout chung, ví dụ main_layout.php (header, footer, menu chung) sử dụng các class của Bootstrap 5 (ví dụ <nav class="navbar navbar-expand-lg bg-light"> hay <div class="container">).
    • views/admin/: giao diện trang admin, ví dụ login.php (form đăng nhập Admin), users_list.php, user_form.php (thêm/sửa user), products_list.php, product_form.php (thêm/sửa sản phẩm). Sử dụng Bootstrap (ví dụ bảng <table class="table table-striped">, form với class form-control, v.v.).
    • views/member/: giao diện cho thành viên, ví dụ login.php (đăng nhập member), profile.php (form thông tin cá nhân), orders.php (danh sách sản phẩm đã mua).
    • views/home/ (hoặc tương tự): trang danh sách sản phẩm chung, ví dụ product_list.php với phân trang.

Ví dụ một đoạn mã mẫu trong controller UserController (Admin) có thể như sau (chỉ minh họa):

<?php
require_once 'models/User.php';
class UserController {
    // Hiển thị danh sách user
    public function index() {
        $users = User::all();  // gọi Model lấy tất cả user
        require 'views/admin/users_list.php'; // render view
    }
    // Thêm user
    public function create() {
        require 'views/admin/user_form.php';
    }
    public function store() {
        // lấy dữ liệu từ $_POST, validate, tạo User mới
        $user = new User($_POST);
        $user->save();  // phương thức Model lưu vào DB
        header("Location: index.php?controller=user&action=index");
    }
    // tương tự edit(), update(), delete()
}
?>

model User.php ví dụ:

<?php
require_once 'config/DB.php';
class User {
    public $id, $name, $email, $password, $role;
    public function __construct($data = []) {
        $this->id = $data['id'] ?? null;
        $this->name = $data['name'] ?? '';
        // v.v.
    }
    public static function all() {
        $db = DB::getInstance();
        $stmt = $db->query("SELECT * FROM users");
        return $stmt->fetchAll(PDO::FETCH_CLASS, 'User');
    }
    public function save() {
        $db = DB::getInstance();
        if ($this->id) {
            // update
            $sql = "UPDATE users SET name=?, email=?, password=? WHERE id=?";
            $stmt = $db->prepare($sql);
            $stmt->execute([$this->name, $this->email, $this->password, $this->id]);
        } else {
            // insert
            $sql = "INSERT INTO users (name,email,password,role) VALUES (?,?,?,?)";
            $stmt = $db->prepare($sql);
            $stmt->execute([$this->name, $this->email, $this->password, $this->role]);
        }
    }
    public static function find($id) {
        $db = DB::getInstance();
        $stmt = $db->prepare("SELECT * FROM users WHERE id=?");
        $stmt->execute([$id]);
        return $stmt->fetchObject('User');
    }
    public function delete() {
        $db = DB::getInstance();
        $stmt = $db->prepare("DELETE FROM users WHERE id=?");
        $stmt->execute([$this->id]);
    }
}
?>

Các controller và model khác (Product, Order, v.v.) tương tự. Controller sẽ gọi Model để thao tác CSDL và gọi view để render. View sử dụng Bootstrap 5 để trình bày giao diện (ví dụ bảng danh sách, form có class form-controlbtn btn-primary,…).

Chức năng chính chi tiết

1. Phần Admin

  • Đăng nhập Admin: Trang login riêng cho admin (form nhập email/mật khẩu). Controller kiểm tra thông tin trong bảng users với điều kiện role='admin'. Mật khẩu đã lưu ở DB nên sử dụng password_verify() để so sánh mật khẩu nhập và mật khẩu băm (xem bên dưới). Sau khi đăng nhập, lưu thông tin admin (ví dụ $_SESSION['admin_name'], $_SESSION['admin_role']='admin') để xác thực ở các trang sau.
  • Quản lý User (CRUD): Admin có thể tạo, sửa, xóa user (loại member hoặc admin). Tại controller, mỗi action tương ứng gọi các method trên Model User và view forms. Ví dụ, chức năng thêm user sẽ hiển thị form (views/admin/user_form.php), nhận dữ liệu từ POST, sau đó gọi $user->save(). Thực chất CRUD (Create-Read-Update-Delete) là các thao tác cơ bản trên CSDL (tạo mới, đọc, cập nhật, xoá bản ghi).
  • Quản lý Sản phẩm (CRUD): Tương tự, Admin có trang hiển thị danh sách sản phẩm (views/admin/products_list.php) và các form thêm/sửa (product_form.php). Controller ProductController có các method index(), create(), store(), edit(), update(), delete() tương tự như UserController. Model Product xử lý truy vấn bảng products.
  • Yêu cầu bảo mật: Chỉ admin mới truy cập được trang quản lý. Trong mỗi Controller admin (hoặc BaseController cha), kiểm tra $_SESSION['role']=='admin' hay tương tự để ngăn chặn truy cập trái phép. Nếu chưa đăng nhập, tự động chuyển về trang login admin.

2. Phần Frontend (giao diện người dùng)

  • Trang danh sách sản phẩm: Trang (views/home/product_list.php) hiển thị tất cả sản phẩm với phân trang và tìm kiếm. Tại HomeController::productList(), thực hiện truy vấn với LIMIT và OFFSET để phân trang, và có thể dùng điều kiện LIKE để tìm kiếm tên sản phẩm. Ví dụ:

    $perPage = 10; // số sản phẩm mỗi trang
    $page = $_GET['page'] ?? 1;
    $start = ($page - 1) * $perPage;
    $keyword = $_GET['search'] ?? '';
    if ($keyword) {
        $stmt = $db->prepare("SELECT * FROM products WHERE name LIKE ? LIMIT ?, ?");
        $stmt->execute(["%$keyword%", $start, $perPage]);
    } else {
        $stmt = $db->prepare("SELECT * FROM products LIMIT ?, ?");
        $stmt->execute([$start, $perPage]);
    }
    $products = $stmt->fetchAll(PDO::FETCH_CLASS, 'Product');

    Sau đó tính tổng số trang và tạo các link phân trang. Phân trang giúp “người dùng duyệt các phần có lượng dữ liệu lớn trên website” bằng cách chia danh sách dài thành nhiều trang con. Nút tìm kiếm (form GET) lọc sản phẩm. Bootstrap 5 có thể dùng pagination component để hiển thị link trang.

  • Đăng nhập Member: Trang login riêng (views/member/login.php) cho thành viên (role = member). Cơ chế tương tự admin: kiểm tra tài khoản trong bảng users với role='member'. Ví dụ xử lý trong controller:

    if ($_SERVER['REQUEST_METHOD']=='POST') {
        $user = User::findByEmail($_POST['email']);  // tự định nghĩa phương thức tìm user theo email
        if ($user && password_verify($_POST['password'], $user->password)) {
            // Lưu thông tin phiên làm việc
            $_SESSION['user_id'] = $user->id;
            $_SESSION['user_name'] = $user->name;
            $_SESSION['user_role'] = $user->role; // ví dụ 'member'
            // Chuyển hướng sau login
        } else {
            $error = "Thông tin đăng nhập không đúng";
        }
    }

    (Ví dụ tham khảo ở StackOverflow: dữ liệu truy vấn ra và gán vào $_SESSION, sau đó password_verify so sánh mật khẩu.)

  • Sau khi đăng nhập (Member): Thành viên đã đăng nhập có thể:

    • Xem sản phẩm đã mua: Ví dụ tạo trang my_orders.php hiển thị các đơn hàng của user. Controller truy vấn bảng orders (hoặc bảng liên kết user-product) với điều kiện user_id = $_SESSION['user_id'] để liệt kê sản phẩm đã mua.
    • Cập nhật thông tin cá nhân: Trang profile.php cho phép xem/sửa thông tin cá nhân (họ tên, email, mật khẩu). Đơn giản là một form POST, controller sẽ cập nhật bảng users. Khi thay đổi mật khẩu, nhớ dùng password_hash() để băm mật khẩu mới trước khi lưu. Hàm password_hash() “tạo một mật khẩu băm với thuật toán hashing một chiều mạnh”. Khi đăng nhập (ở trên), dùng password_verify() để so sánh mật khẩu nhập với mật khẩu lưu.

Ví dụ mã HTML/Bootstrap (mô tả)

Các trang view sử dụng Bootstrap 5 để giao diện đẹp, responsive. Ví dụ, một form login admin có thể viết như sau:

<!-- view/admin/login.php -->
<form method="POST" action="index.php?controller=admin&action=login">
  <div class="mb-3">
    <label for="email" class="form-label">Email:</label>
    <input type="email" name="email" class="form-control" id="email" required>
  </div>
  <div class="mb-3">
    <label for="password" class="form-label">Mật khẩu:</label>
    <input type="password" name="password" class="form-control" id="password" required>
  </div>
  <button type="submit" class="btn btn-primary">Đăng nhập</button>
</form>

Danh sách sản phẩm hoặc user thường được hiển thị bằng table của Bootstrap (<table class="table table-striped">). Ví dụ, view users_list.php có thể lặp qua mảng $users và hiển thị tên, email, role, cùng các nút Sửa/Xóa. Tương tự, product_list.php hiển thị tên, giá, và các chức năng phân trang ở dưới (<ul class="pagination">...).

Hướng dẫn cài đặt và chạy project

  1. Chuẩn bị môi trường: Cài đặt XAMPP (hoặc tương đương). Mở XAMPP Control Panel, bật Apache và MySQL.

  2. Tải project: Copy toàn bộ mã nguồn bài lab vào thư mục trong htdocs (ví dụ xampp/htdocs/ql_banhang).

  3. Tạo và nhập database: Dùng phpMyAdmin (http://localhost/phpmyadmin) tạo database lab_db, và thực thi file SQL (nếu có) để tạo các bảng users, products, orders,… với cấu trúc phù hợp. Có thể dùng ví dụ trong bài hoặc tự tạo.

  4. Cấu hình kết nối DB: Mở file config/DB.php hoặc connection.php, sửa thông số theo máy mình: host, dbname, username, password. Ví dụ:

    new PDO('mysql:host=localhost;dbname=lab_db;charset=utf8', 'root', '');
  5. Chạy ứng dụng: Truy cập http://localhost/ql_banhang/index.php (hoặc chỉ index.php?controller=pages&action=home tùy routing). Trang Admin login thường ở đường dẫn như index.php?controller=admin&action=login, member login ở index.php?controller=auth&action=loginMember.

Nếu thấy trang trắng hoặc lỗi, kiểm tra các bước trên. Đảm bảo file .htaccess nếu có (để rewrite URL) đã bật mod_rewrite và đặt đúng.

Tổng kết và gợi ý

  • MVC pattern: Giúp tách riêng phần xử lý (Controller), logic/dữ liệu (Model) và giao diện (View). Trong code, Controller nhận request, gọi Model thao tác CSDL, rồi gửi dữ liệu qua View để hiển thị.

  • CRUD: là các thao tác cơ bản (Create, Read, Update, Delete) trên dữ liệu. Hầu hết các trang quản lý danh sách đều yêu cầu CRUD, ví dụ trang quản lý users sử dụng CRUD trên bảng users.

  • Bảo mật: Luôn dùng password_hash() để lưu mật khẩu (ví dụ PASSWORD_DEFAULT) và password_verify() khi đăng nhập. Sử dụng $_SESSION để lưu trạng thái đăng nhập và kiểm tra quyền (admin vs member) mỗi lần vào trang cần bảo vệ. Ví dụ, trong AdminController bạn có thể kiểm tra:

    session_start();
    if ($_SESSION['role'] !== 'admin') {
        header('Location: index.php?controller=admin&action=login');
        exit;
    }
  • Bootstrap 5: Để trang web đẹp và responsive, nạp CSS/JS của Bootstrap (thường bằng CDN) trong layout chung (views/layouts/main_layout.php), và dùng các class như container, row, col, btn, form-control, table, pagination… để tạo giao diện theo hướng mobile-first.

  • Tham khảo: Ví dụ các hướng dẫn MVC trên Viblo, các tutorial CRUD dùng PHP và Bootstrap, cũng như tài liệu chính thức của PHP để xử lý sessions và PDO.

Bằng cách tổ chức theo MVC như trên, thực tập sinh có thể dễ dàng hiểu luồng chương trình, tách biệt rõ các phần code và mở rộng tính năng. Khi triển khai, hãy lần lượt thực hiện từng phần (đầu tiên là cấu trúc thư mục và kết nối DB, rồi tiếp đến viết chức năng login, sau đó CRUD, cuối cùng là giao diện). Các trích dẫn trên đây giúp bạn nắm rõ khái niệm MVC và cách sử dụng các công nghệ PHP/MySQL/Bootstrap trong dự án.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment