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.
-
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 Apache và MySQL. 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ảngusers
(có cột id, name, email, password, role, …), bảngproducts
(id, name, price, description, …), và bảngorders
hoặc tương tự để lưu sản phẩm đã mua của từng user (ví dụ bảngorders
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ặcconnection.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êmindex.php
). Nếu dùng hệ thống routing với.htaccess
, cần bật mod_rewrite. Thường thì fileindex.php
sẽ xử lý tham sốcontroller
vàaction
từ URL để gọi Controller tương ứng.
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ặcconnection.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ạoOrder.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 classform-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()
}
?>
Và 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-control
và btn btn-primary
,…).
- Đă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ệnrole='admin'
. Mật khẩu đã lưu ở DB nên sử dụngpassword_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ặcadmin
). Tại controller, mỗi action tương ứng gọi các method trên ModelUser
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
). ControllerProductController
có các methodindex()
,create()
,store()
,edit()
,update()
,delete()
tương tự nhưUserController
. ModelProduct
xử lý truy vấn bảngproducts
. - 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.
-
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ạiHomeController::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ảngusers
vớirole='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ảngorders
(hoặc bảng liên kết user-product) với điều kiệnuser_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ảngusers
. Khi thay đổi mật khẩu, nhớ dùngpassword_hash()
để băm mật khẩu mới trước khi lưu. Hàmpassword_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ùngpassword_verify()
để so sánh mật khẩu nhập với mật khẩu lưu.
- Xem sản phẩm đã mua: Ví dụ tạo trang
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">...
).
-
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.
-
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
). -
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ảngusers
,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. -
Cấu hình kết nối DB: Mở file
config/DB.php
hoặcconnection.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', '');
-
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.
-
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ụ, trongAdminController
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.