Từng bước học lập trình PHP nâng cao qua dự án website giới thiệu sản phẩm

filiallion

Thành viên thân thiết
Thành viên thân thiết
Tham gia
22/10/2014
Bài viết
136
Sau loạt bài hướng dẫn "Từng bước học lập trình PHP cơ bản qua dự án website giới thiệu sản phẩm" tôi sẽ tiếp tục viết loạt bài hướng dẫn các bạn học lập trình PHP nâng cao (Lập trình hướng đối tượng) thông qua việc hoàn thành dự án website giới thiệu sản phẩm (Tương tự như ở loạt bài cơ bản).

Từng bước hoàn thành dự án website giới thiệu sản phẩm (Trang quản trị và trang người dùng) các bạn sẽ được học các vấn đề liên quan tới lập trình PHP nâng cao (Lập trình hướng đối tượng).

Các bạn lưu ý, bài hướng dẫn này tôi chỉ trình bày và giải thích các vấn đề nâng cao trong lập trình PHP. Các vấn đề tôi đã trình bày, giải thích ở loạt bài cơ bản rồi thì tôi sẽ không trình bày hay giải thích cho các bạn nữa. Và tôi cũng khuyên các bạn nên tìm hiểu, nghiên cứu thật kỹ phần cơ bản trước khi tìm hiểu phần nâng cao này.

Loạt bài hướng dẫn "Từng bước học lập trình PHP nâng cao qua dự án website giới thiệu sản phẩm" tôi đã đăng trên trang cá nhân Google+ của tôi tại địa chỉ https://plus.google.com/103116221369826139041.


Trong quá trình theo dõi bài hướng dẫn các bạn có thể tham gia thảo luận và đóng góp ý kiến để bài viết ngày càng hoàn thiện hơn.

Chân thành cảm ơn sự quan tâm của các bạn!
 
Hiệu chỉnh bởi quản lý:
THÔNG TIN CẤU HÌNH WEBSITE (PHẦN 1 + 2)

Về cấu trúc thư mục, cấu trúc DB và các chức năng của website tương tự như ở loạt bài hướng dẫn "Từng bước học lập trình PHP cơ bản qua dự án website giới thiệu sản phẩm".

Nội dung kiến thức phần này gồm:

- Lớp (Class).
- Hằng.

Trong thư mục configs, tạo mới file Config.php để khai báo các thông tin cấu hình website. Bạn lưu ý file Config.php tôi viết hoa chữ "C". Chuẩn đặt tên file, lớp, hàm, biến... các bạn lưu ý các nội dung hướng dẫn của tôi.

Nội dung file configs/Config.php như sau:
PHP:
<?php
class Config {
//Thông tin website
const SITE_URL = '[URL]https://localhost/[/URL]';
const BASE_PATH = 'C:\Wamp\www\\';

//Thông tin cấu hình DB
const DB_SERVER = 'localhost';
const DB_USERNAME = 'root';
const DB_PASSWORD = '';
const DB_DATABASE = 'php_2';
}
?>

* Lớp (Class):

Lớp các bạn có thể hiểu như một bản thiết kế (Hay khuôn mẫu) của một đối tượng nào đó với các thuộc tính, trạng thái, hành động... theo mục đích của người lập trình.

Bên trong một lớp có các thành phần:

- Thuộc tính (Property): Mô tả thuộc tính của đối tượng. Thuộc tính của một lớp bao gồm các biến, các hằng, hay tham số nội tại của lớp đó. Ở đây, vai trò quan trọng nhất của các thuộc tính là các biến vì chúng có thể bị thay đổi trong suốt quá trình hoạt động của một đối tượng. Các thuộc tính có thể được xác định kiểu và kiểu của chúng có thể là các kiểu dữ liệu cổ điển hay đó là một lớp đã định nghĩa từ trước.
- Phương thức (Method): Mô tả một hành vi nào đó của đối tượng (Hoặc của lớp). Phương thức thực chất như một hàm bên trong một lớp.

Ví dụ:

Lớp "Người" dùng để chỉ những thực thể sống trên trái đất với các thuộc tính là "chân, tay, mắt, mũi..." và có những hành vi như "đi, đứng, nằm...".

Để tạo ra một lớp, các bạn thực hiện theo mẫu sau:

class Tên_lớp {
//Nội dung của lớp
}

Ví dụ:
PHP:
class Person {
}

Các bạn lưu ý khi đặt tên lớp:

- Tên lớp trùng với tên file PHP (Vì vậy tên file PHP cũng phải tuân thủ quy tắc đặt tên của lớp).
- Nếu tên lớp có từ hai từ trở lên thì phải viết hoa đầu từ và có thể viết liền nhau hoặc sử dụng dấu gạch dưới "_" để phân cách (Ví dụ: Person, UserProfile hoặc User_Profile).

Liên quan tới lớp còn có khái niệm đối tượng (Object), thuộc tính, phương thức.. tôi sẽ trình bày sau.

Các bạn có thể tìm hiểu thêm về lớp ở liên kết https://php.net/manual/en/keyword.class.php.

* Hằng:

Hằng là gì thì tôi đã trình bày ở loạt bài hướng dẫn "Từng bước học lập trình PHP cơ bản qua dự án website giới thiệu sản phẩm". Ở phần này thì tôi chỉ trình bày cho các bạn biết cách khai báo, sử dụng hằng trong một lớp.

Ở file configs/Config.php, tôi đã khai báo 6 hằng (SITE_URL, BASE_PATH, DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_DATABASE). Như vậy, để khai báo hằng trong một lớp các bạn thực hiện theo mẫu sau:

const Tên_hằng = Giá_trị_của_hằng;

Ví dụ:
PHP:
const SITE_URL = '[URL]https://localhost/[/URL]';

Hằng cũng có thể xem như một thuộc tính của lớp nhưng giá trị của hằng là không thay đổi.

Để gọi (Sử dụng) hằng các bạn thực hiện theo mẫu sau:

Tên_lớp::Tên_hằng;

Ví dụ:
PHP:
Config::SITE_URL;

Bạn có thể tìm hiểu thêm về hằng ở liên kết https://php.net/manual/en/language.oop5.constants.php.
 
KẾT NỐI DB (PHẦN 3)

Nội dung kiến thức phần này gồm:

- Thuộc tính (Property).
- Phương thức (Method).
- Kế thừa (Extends).

Trong thư mục libraries, bạn tạo mới hai file MySql.php và Db.php để viết mã lệnh kết nối DB.

Nội dung file libraries/MySql.php như sau:
PHP:
<?php
class MySql {
protected $_query;

public function __construct(){
//Connect
$connection = mysql_pconnect(Config::DB_SERVER, Config::DB_USERNAME, Config::DB_PASSWORD) or die('Not connected DB!');
$dbSelected = mysql_select_db(Config::DB_DATABASE, $connection) or die('Not selected DB!');

//Set UTF8
mysql_query('SET NAMES UTF8', $connection);
}

public function query($sql){
//Truy vấn và return
return $this->_query = mysql_query($sql);
}

public function fetch(){
//Fetch và return
return mysql_fetch_assoc($this->_query);
}
}
?>

Nội dung file libraries/Db.php như sau:
PHP:
<?php
require BASE_PATH . 'libraries/MySql.php';

class Db extends MySql {
}
?>

Như các bạn thấy, toàn bộ các câu lệnh kết nối tới DB đều nằm ở lớp MySql được viết trong file libraries/MySql.php. Vậy lớp Db được viết trong file libraries/Db.php có vai trò gì khi mà lớp Db này chỉ mở rộng (Extends) từ lớp MySql chứ không có thêm thuộc tính hay phương thức nào mới? Lớp Db này có một vai trò quan trọng mà tôi hi vọng các bạn sẽ tìm hiểu thêm.

Các bạn lưu ý tới hai phương thức query và fetch của lớp MySql. Trong đó:

- Phương thức query dùng để thực thi câu lệnh SQL và gán kết quả vào thuộc tính $_query để sử dụng ở phương thức fetch (Trong trường hợp truy vấn lấy dữ liệu).
- Phương thức fetch dùng để lấy từng dòng dữ liệu theo kết quả truy vấn được lưu ở thuộc tính $_query (Trong trường hợp truy vấn lấy dữ liệu).

* Thuộc tính (Property):

Để khai báo thuộc tính trong lớp, các bạn thực hiện theo mẫu sau:

Phạm_vi $Tên_thuộc_tính;

Hoặc

Phạm_vi $Tên_thuộc_tính = Giá_trị_của_thuộc_tính;

Thuộc tính trong lớp phải có phạm vị hoạt động (Tầm vực) và được thể hiện qua ba từ khóa phạm vi sau:

- Public: Có phạm vị sử dụng "rộng" nhất và không bị giới hạn. Nếu các bạn không khai báo phạm vi cho thuộc tính thì mặc định thuộc tính đó là public.
- Protected: Có phạm vi hoạt động bên trong nội tại của lớp và các lớp kế thừa (Còn gọi là lớp con hoặc lớp dẫn xuất).
- Private: Có phạm vi hoạt động "hẹp" nhất, chỉ hoạt động bên trong nội tại của lớp.

Ví dụ:
PHP:
public $a;
protected $_b;
private $_c;

Các bạn lưu ý về cách đặt tên thuộc tính (Áp dụng cho cả tên biến):

- Tuân theo các qui tắc đặt tên biến.
- Từ đầu tiên là chữ thường. Nếu có từ hai từ trở lên thì chữ cái đầu tiên của từ thứ hai trở đi là chữ hoa (Ví dụ: $user, $userProfile, $deltailUserProfile).
- Những thuộc tính có phạm vi protected và private thì tên thuộc tính bắt đầu bằng dấu gạch dưới "_" (Ví dụ: $_query).

Để gọi (Sử dụng) thuộc tính đã được khai báo các bạn thực hiện theo mẫu sau:

$this->Tên_thuộc_tính;

Hoặc

self::$Tên_thuộc_tính;

Hoặc

parent::$Tên_thuộc_tính;

Ví dụ:
PHP:
$this->a;
self::$_b;
parent::$_c;

Với ba mẫu trên, tùy từng trường hợp các bạn sử dụng cho đúng chứ không phải muốn sử dụng mẫu nào cũng được.

Mẫu thứ nhất được dùng khi gọi một thuộc tính của chính lớp đó hoặc lớp cơ sở (Còn gọi là lớp cha).

Mẫu thứ hai được dùng khi gọi một thuộc tính của chính lớp đó.

Mẫu thứ ba được dùng khi gọi một thuộc tính của lớp cơ sở.

Trình bày thì đơn giản như vậy nhưng khi sử dụng thì tùy từng mục đích, yêu cầu mà các bạn sử dụng cho phù hợp. Ngoài ra, còn có một cách sử dụng nữa dành cho thuộc tính có phạm vi public tôi sẽ trình bày sau.

Các bạn có thể tìm hiểu thêm về thuộc tính ở liên kết https://php.net/manual/en/language.oop5.properties.php.

* Phương thức (Method):

Để khai báo phương thức trong lớp, các bạn thực hiện theo mẫu sau:

Phạm_vi function Tên_phương_thức(){
//Nội dung phương thức
}

Hoặc

Phạm_vi function Tên_phương_thức($Tham_số_1, $Tham_số_2, $Tham_số_n){
//Nội dung phương thức
}

Ví dụ:
PHP:
public function query($sql){
}

Phạm vi của phương thức hoàn toàn tương tự với phạm vi của thuộc tính.

Quy tắc đặt tên phương thức cũng tương tự như thuộc tính.

Để gọi (Sử dụng) phương thức đã khai báo các bạn thực hiện theo mẫu sau:

$this->Tên_phương_thức();

Hoặc

self::Tên_phương_thức();

Hoặc

parent::Tên_phương_thức();

Ví dụ:
PHP:
$this->query('SQL');
self::fetch();
parent::get();

Ba cách sử dụng trên cũng hoàn toàn tương tự như thuộc tính.

Ngoài các phương thức do người lập trình định nghĩa, PHP còn cung cấp các phương thức "magic" như: __construct, __destruct... Các bạn có thể tìm hiểu thêm về các phương thức "magic" ở liên kết https://php.net/manual/en/language.oop5.magic.php.

Thuộc tính và phương thức còn nhiều kiến thức nâng cao khác mà tôi không trình bày cho các bạn ở đây. Trong quá trình tìm hiểu, các bạn có vấn đề cần hỗ trợ thì các bạn có thể hỏi và tôi sẽ giải đáp cho các bạn.

* Kế thừa (Extends):

Trong PHP, các bạn có thể mở rộng lớp, hay nói một cách khác, một lớp có thể được kế thừa từ một lớp nào đó đã được khai báo trước. Trong đó, lớp được kế thừa gọi là lớp cơ sở (Lớp cha), lớp kế thừa gọi là lớp dẫn xuất (Lớp con). Lớp dẫn xuất có quyền sử dụng các thuộc tính, phương thức có phạm vi public và protected của lớp cơ sở.

Một lớp có thể kế thừa các thuộc tính và phương thức của lớp khác, bằng cách sử dụng từ khóa extends. Các bạn có thể khai báo kế thừa theo mẫu sau:

class Lớp_dẫn_xuất extends Lớp_cơ_sở {
}

Ví dụ:
PHP:
class Db extends MySql {
}

Với ví dụ trên, lớp Db được gọi là lớp dẫn xuất, lớp MySql được gọi là lớp cơ sở. Toàn bộ các thuộc tính, phương thức có phạm vi public và protected của lớp MySql (Lớp cơ sở) đều được lớp Db (Lớp dẫn xuất) kế thừa (Tức có quyền sử dụng).

Các bạn có thể tìm hiểu thêm về kế thừa (Chủ yếu là nói về từ khóa extends) ở liên kết https://php.net/manual/en/keyword.extends.php.

Kế thừa và các kỹ thuật lập trình liên quan tới kế thừa các bạn có thể tìm hiểu thêm để mở rộng kiến thức.
 
TRANG ĐĂNG NHẬP, ĐĂNG XUẤT (PHẦN 4)

Từ phần này, tôi sẽ hướng dẫn các bạn thực hiện các trang trong phần quản trị (Admin).

Các bạn lưu ý là trang chủ quản trị tôi sẽ không hướng dẫn các bạn.

Nội dung kiến thức phần này gồm:

- Đối tượng (Object).

Trong thư mục admin, các bạn tạo mới thư mục tên là user để chứa các file xử lý liên quan tới thành viên (Đăng nhập, đăng xuất, danh sách, thêm mới, chỉnh sửa).

Trong thư mục admin/user, tạo mới file login.php để viết mã lệnh xử lý đăng nhập.

Nội dung file admin/user/login.php như sau:
PHP:
<?php
//Khởi động session
session_start();

//Kiểm tra nếu đã đăng nhập thì quay về trang chủ quản trị
if(isset($_SESSION['user'])){
header('location:../home/home.php');
}

//Require các file cần thiết
require '../../configs/Config.php';
require '../../models/User.php';

//Kiểm tra dữ liệu POST lên
if(isset($_POST['username']) && !empty($_POST['username']) && isset($_POST['password']) && !empty($_POST['password'])){
//Gán tài khoản và mật khẩu nhận được từ form vào 2 biến tương ứng
$username = $_POST['username'];
$password = $_POST['password'];

//Khởi tạo đối tượng thành viên (User)
$userModel = new User();

//Lấy thông tin thành viên
$user = $userModel->getByUsername($username);

//Kiểm tra sự tồn tại của thành viên và mật khẩu có trùng khớp
if($user && $user->getPassword() === md5($password)){
//Tạo session lưu thông tin thành viên đăng nhập thành công
$_SESSION['user'] = $user;

//Chuyển hướng về trang chủ quản trị
header('location:../home/home.php');
}else{
//Bật cờ lỗi
$error = true;
}
}

//Require file giao diện (View)
require '../../views/admin/user/login.tpl.php';
?>

Trong thư mục models các bạn tạo mới hai file UserObj.php và User.php để thể hiện đối tượng thành viên (Lớp UserObj) và tương tác với DB (Lớp User).

Nội dung file models/UserObj.php như sau:
PHP:
<?php
class UserObj {
protected $userId;
protected $username;
protected $password;
protected $fullname;
protected $email;
protected $status;
protected $created;
protected $modified;

public function setUserId($userId){
$this->userId = $userId;
}

public function getUserId(){
return $this->userId;
}

public function setUsername($username){
$this->username = $username;
}

public function getUsername(){
return $this->username;
}

public function setPassword($password){
$this->password = $password;
}

public function getPassword(){
return $this->password;
}

public function setFullname($fullname){
$this->fullname = $fullname;
}

public function getFullname(){
return $this->fullname;
}

public function setEmail($email){
$this->email = $email;
}

public function getEmail(){
return $this->email;
}

public function setStatus($status){
$this->status = $status;
}

public function getStatus(){
return $this->status;
}

public function setCreated($created){
$this->created = $created;
}

public function getCreated(){
return $this->created;
}

public function setModified($modified){
$this->modified = $modified;
}

public function getModified(){
return $this->modified;
}
}
?>

Nội dung file models/User.php như sau:
PHP:
<?php
require_once Config::BASE_PATH . 'libraries/Db.php';
require_once Config::BASE_PATH . 'models/UserObj.php';

class User {
protected $db;

public function __construct(){
$this->db = new Db();
}

public function getByUsername($username){
//SQL
$sql = "SELECT * FROM tbl_user WHERE username = '$username' AND status = 1";

//Query
$this->db->query($sql);

//Fetch
$row = $this->db->fetch();

//Khởi tạo đối tượng UserObj
$userObj = new UserObj();

//Gán thông tin
$userObj->setUserId($row['user_id']);
$userObj->setUsername($row['username']);
$userObj->setPassword($row['password']);
$userObj->setFullname($row['fullname']);
$userObj->setEmail($row['email']);
$userObj->setStatus($row['status']);
$userObj->setCreated($row['created']);
$userObj->setModified($row['modified']);

//Return
return $userObj;
}
}
?>

Trong thư mục views/admin, các bạn tạo mới thư mục tên là user để chứa các file trình bày giao diện HTML trang thành viên.

Trong thư mục views/admin/user, tạo mới file login.tpl.php để trình bày giao diện trang đăng nhập.

Nội dung file views/admin/user/login.tpl.php như sau:
HTML:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Quản trị - Đăng nhập</title>
</head>
<body>

<form name="login" method="POST" action="">
<?php if(isset($error) && $error == true): ?>
<p style="color:red;">Sai Tài khoản hoặc Mật khẩu!</p>
<?php endif; ?>

<p>
<label>Tài khoản:</label>
<input type="text" name="username" value="" />
</p>
<p>
<label>Mật khẩu:</label>
<input type="password" name="password" value="" />
</p>
<p>
<input type="submit" value="Đăng nhập" />
</p>
</form>

</body>
</html>

Bây giờ các bạn vào phpMyAdmin để thêm mới một dòng dữ liệu vào bảng tbl_user với các thông tin như: Tài khoản, mật khẩu (Mã hóa MD5), kích hoạt (Nhập giá trị là 1)... rồi bắt đầu truy cập trang đăng nhập theo địa chỉ https://localhost/admin/user/login.php để kiểm tra.

Đối với trang đăng xuất, trong thư mục admin/user các bạn tạo mới file logout.php để xử lý đăng xuất.

Nội dung file admin/user/logout.php như sau:
PHP:
<?php
//Khởi động session
session_start();

//Hủy toàn bộ session
session_destroy();

//Quay về trang đăng nhập
header('location:login.php');
?>

Trang đăng xuất không có giao diện hay truy cập DB nên mã lệnh chỉ đơn giản như vậy.

* Đối tượng (Object):

Đối tượng có thể xem như một "thực thể" của lớp. Trong thực tế, các đối tượng thường được trừu tượng hóa qua việc định nghĩa của các lớp. Trong đó:

- Tập hợp các giá trị hiện có của các thuộc tính tạo nên trạng thái của một đối tượng.
- Mỗi phương thức hay mỗi dữ liệu nội tại cùng với các tính chất được định nghĩa được xem là một đặc tính riêng của đối tượng.

Như vậy có thể hiểu một đối tượng được tạo ra từ một lớp và mang các đặc điểm, trạng thái của lớp đó. Sự khác biệt giữa lớp và đối tượng là không rõ ràng, vì vậy để phân biệt hai khái niệm này đòi hỏi các bạn phải có kinh nghiệm trong lập trình hướng đối tượng (OOP).

Để khởi tạo một đối tượng các bạn thực hiện theo mẫu sau:

$Tên_đối_tượng = new Tên_lớp();

Ví dụ:
PHP:
$userObj = new UserObj();

Đối tượng được tạo ra từ một lớp có quyền truy cập (Sử dụng) các thuộc tính và phương thức có phạm vi public của lớp đó. Để truy cập các thuộc tính, phương thức của lớp các bạn thực hiện theo mẫu sau:

$Tên_đối_tượng->Tên_thuộc_tính;
$Tên_đối_tượng->Tên_phương_thức();

Ví dụ:
PHP:
$obj = new Obj();
$obj->propertyOne;
$obj->methodOne();

Với ví dụ trên thì các bạn biết chắc chắn rằng thuộc tính propertyOne và phương thức methodOne đều có phạm vi public.

Các bạn có thể tìm hiểu thêm về đối tượng ở liên kết https://php.net/manual/en/language.types.object.php.
 
TRANG DANH SÁCH THÀNH VIÊN (PHẦN 5)

Từ phần này tôi sẽ trình bày cho các bạn các kiến thức PHP nâng cao mà không liên quan tới dự án website tôi đang hướng dẫn các bạn thực hiện.

Nội dung kiến thức phần này gồm:

- Biểu thức chính quy (Regular expressions).

Trong thư mục admin/user, tạo mới file list.php để viết mã lệnh trang danh sách thành viên.

Nội dung file admin/user/list.php như sau:
PHP:
<?php
//Khởi động session
session_start();

//Kiểm tra nếu chưa đăng nhập thì quay về trang đăng nhập
if(!isset($_SESSION['user'])){
header('location:login.php');
}

//Require các file cần thiết
require '../../configs/Config.php';
require '../../models/User.php';

//Khởi tạo đối tượng thành viên (User)
$userModel = new User();

//Lấy danh sách thành viên
$userList = $userModel->getList();

//Tiêu đề trang
$title = 'Thành viên - Danh sách';

//View
$view = 'user/list.tpl.php';

//Require layout
require '../../views/admin/layout.tpl.php';
?>

Mở file models/User.php và thêm vào lớp User khối lệnh mới dưới đây:
PHP:
public function getList(){
//SQL
$sql = "SELECT * FROM tbl_user ORDER BY user_id DESC";

//Query
$this->db->query($sql);

//Tạo mãng lưu trữ
$listUser = array();

//Fetch
while($row = $this->db->fetch()){
//Khởi tạo đối tượng UserObj
$userObj = new UserObj();

//Gán thông tin
$userObj->setUserId($row['user_id']);
$userObj->setUsername($row['username']);
$userObj->setPassword($row['password']);
$userObj->setFullname($row['fullname']);
$userObj->setEmail($row['email']);
$userObj->setStatus($row['status']);
$userObj->setCreated($row['created']);
$userObj->setModified($row['modified']);

//Gán vào mãng lưu trữ
$listUser[] = $userObj;
}

//Return
return $listUser;
}

Về phần giao diện (View) của website, tôi mở rộng thêm phần layout (Bộ khung) của website. Layout là phần giao diện HTML chung của toàn bộ các trang (Trừ trang đăng nhập). Mục đích của việc tạo ra layout là để giúp cho việc lập trình, bảo trì đơn giản, khoa học hơn.

Trong thư mục views/admin, tạo mới file layout.tpl.php để trình bày layout của website.

Nội dung file views/admin/layout.tpl.php như sau:
HTML:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Quản trị - <?php echo $title; ?></title>
</head>
<body>

<?php require Config::BASE_PATH . 'views/admin/' . $view; ?>

</body>
</html>

Trong thư mục views/admin/user, tạo mới file list.tpl.php để trình bày giao diện trang danh sách thành viên.

Nội dung file views/admin/user/list.tpl.php như sau:
HTML:
<table width="100%" cellpadding="10">
<tr>
<th>ID</th>
<th>Tài khoản</th>
<th>Họ tên</th>
<th>Trạng thái</th>
<th>Ngày tạo</th>
<th>Ngày chỉnh sửa</th>
<th>Tác vụ</th>
</tr>
<?php foreach($userList as $user): ?>
<tr>
<td><?php echo $user->getUserId(); ?></td>
<td><a href="<?php echo Config::SITE_URL . 'admin/user/edit.php?user_id=' . $user->getUserId(); ?>"><?php echo $user->getUsername(); ?></a></td>
<td><?php echo $user->getFullname(); ?></td>
<td><?php echo ($user->getStatus() == 1) ? 'Kích hoạt' : 'Không kích hoạt'; ?></td>
<td><?php echo date('d-m-Y H:i:s', $user->getCreated()); ?></td>
<td><?php echo date('d-m-Y H:i:s', $user->getModified()); ?></td>
<td><a href="<?php echo Config::SITE_URL . 'admin/user/delete.php?user_id=' . $user->getUserId(); ?>">Xóa</a></td>
</tr>
<?php endforeach; ?>
</table>

Các bạn truy cập trang danh sách thành viên theo địa chỉ https://localhost/admin/user/list.php để kiểm tra.

* Biểu thức chính quy (Regular expressions):

Biểu thức chính quy là một chuỗi hoặc một mẫu các ký tự miêu tả một bộ các chuỗi khác.

Biểu thức chính quy thường được sử dụng trong trường hợp tìm kiếm một chuỗi bên trong một chuỗi khác, thay thế một chuỗi bằng chuỗi khác và có thể tách một chuỗi thành nhiều chuỗi.

Nếu bạn sử dụng tốt những kỹ năng về biểu thức chính quy. Chúng sẽ đơn giản hơn nhiều trong lập trình và quá trình xử lý văn bản, có những vấn đề sẽ không thể giải quyết được nếu không sử dụng biểu thức chính quy.

Về biểu thức chính quy, các bạn có thể tìm hiểu thêm về khái niệm, nguồn gốc của nó trên mạng. Và tôi khuyên các bạn nên sử dụng biểu thức chính quy để việc lập trình trở nên đơn giản, tối ưu và chính xác hơn.

Tôi giới thiệu với các bạn một số "cú pháp" về biểu thức chính quy sau đây hi vọng đủ để các bạn có kiến thức tiếp tục tìm hiểu:

- Dấu "ngoặc vuông" ([]): Dấu "ngoặc vuông" có một ý nghĩa đặc biệt khi sử dụng trong bối cảnh của biểu thức thông thường. Dấu "ngoặc vuông" thường được dùng khi các bạn muốn tìm kiếm một loạt các ký tự theo mẫu đã khai báo bên trong dấu.

Ví dụ:

[0-9]: Phù hợp với bất kỳ các chữ số từ 0 đến 9.
[a-z]: Phù hợp với bất kỳ các ký tự chữ in thường từ a đến z.
[A-Z]: Phù hợp với bất kỳ các ký tự chữ in hoa từ A đến Z.
[0-9a-zA-Z]: Phù hợp với bất kỳ các chữ số từ 0 đến 9, ký tự in thường và in hoa từ a đến z.

- Các ký hiệu định lượng: Các bạn cần quan tâm tới các ký hiệu sau: +, *, ?, {} và $. Các ký hiệu này biểu thị số lượng hoặc vị trí hoặc một ký tự cụ thể nào đó.

Ví dụ:

p+: Phù hợp với bất kỳ chuỗi mà có chứa ít nhất một ký tự p.
p*: Phù hợp với bất kỳ chuỗi mà có chứa số 0 hoặc chứa nhiều hơn ký tự p.
p?: Có ý nghĩa tương tự như p*.
p{5}: Phù hợp với chuỗi có chứa đúng 5 ký tự p.
p{1,5}: Phù hợp với bất kỳ chuỗi mà có chứa từ 1 đến 5 ký tự p.
p{5,}: Phù hợp với bất kỳ chuỗi mà có chứa ít nhất 5 ký tự p.
p$: Phù hợp với bất kỳ chuỗi mà có ký tự p là cuối cùng.
^p: Phù hợp với bất kỳ chuỗi mà có ký tự p là bắt đầu.
[^a-zA-Z]: Phù hợp với bất kỳ chuỗi mà không chứa các ký tự in thường và in hoa từ a đến z.
^.{2}$: Phù hợp với bất kỳ chuỗi có chứa hai ký tự.

Lưu ý là các mẫu ví dụ trên không tính dấu hai chấm (:).

Ngoài loại biểu thức chính quy như trên (POSIX), trong PHP còn có một loại biểu thức chính quy khác gọi là PCRE (Hoặc PERL) các bạn có thể tìm hiểu thêm.

Các bạn có thể tìm hiểu thêm về các hàm PHP xử lý biểu thức chính quy ở liên kết https://php.net/manual/en/ref.regex.phphttps://php.net/manual/en/ref.pcre.php.
 
TRANG THÊM MỚI THÀNH VIÊN (PHẦN 6)

Nội dung kiến thức phần này gồm:

- Xử lý lỗi (Error handling).

Trong thư mục admin/user, tạo mới file add.php để viết mã lệnh trang thêm mới thành viên.

Nội dung file admin/user/add.php như sau:
PHP:
<?php
//Khởi động session
session_start();

//Kiểm tra nếu chưa đăng nhập thì quay về trang đăng nhập
if(!isset($_SESSION['user'])){
header('location:login.php');
}

//Require các file cần thiết
require '../../configs/Config.php';
require '../../models/User.php';

//Nếu có POST dữ liệu lên thì xử lý
if($_POST){
//Nhận dữ liệu từ form và gán vào một mãng (Có thể sử dụng UserObj để lưu dữ liệu)
$data = array(
'username' => $_POST['username'],
'password' => md5($_POST['password']),
'fullname' => $_POST['fullname'],
'email' => $_POST['email'],
'status' => isset($_POST['status']) ? 1 : 0,
'created' => date('Y-m-d H:i:s'),
'modified' => date('Y-m-d H:i:s')
);

//Khởi tạo đối tượng thành viên (User)
$userModel = new User();

//Thêm mới
if($userModel->add($data)){
//Tạo session để lưu cờ thông báo thành công
$_SESSION['success'] = true;

//Tải lại trang (Mục đích là để reset form)
header('location:add.php');

//Ngừng thực thi
exit();
}
}

//Tiêu đề trang
$title = 'Thành viên - Thêm mới';

//View
$view = 'user/add.tpl.php';

//Require layout
require '../../views/admin/layout.tpl.php';
?>

Mở file models/User.php và thêm vào lớp User khối lệnh mới dưới đây:
PHP:
public function add($data){
//SQL
$sql = "INSERT INTO tbl_user(username, password, fullname, email, status, created, modified) VALUES('{$data['username']}', '{$data['password']}', '{$data['fullname']}', '{$data['email']}', {$data['status']}, '{$data['created']}', '{$data['modified']}')";

//Query và return
return $this->db->query($sql);
}

Trong thư mục views/admin/user, tạo mới file add.tpl.php để trình bày giao diện trang thêm mới thành viên.

Nội dung file views/admin/user/add.tpl.php như sau:
HTML:
<form name="add" method="POST" action="">
<?php if(isset($_SESSION['success'])): ?>
<p style="color:green;">Thành viên đã được thêm mới thành công!</p>
<?php unset($_SESSION['success']); ?>
<?php endif; ?>

<p>
<label>Tài khoản:</label>
<input type="text" name="username" value="" />
</p>
<p>
<label>Mật khẩu:</label>
<input type="password" name="password" value="" />
</p>
<p>
<label>Họ tên:</label>
<input type="text" name="fullname" value="" />
</p>
<p>
<label>Email:</label>
<input type="text" name="email" value="" />
</p>
<p>
<label>Trạng thái:</label>
<input type="checkbox" name="status" value="1" />
</p>
<p>
<input type="submit" value="Thêm mới" />
</p>
</form>

Các bạn truy cập trang thêm mới thành viên theo địa chỉ https://localhost/admin/user/add.php để kiểm tra.

* Xử lý lỗi (Error handling):

Xử lý lỗi là quá trình bắt lỗi chương trình (Khối lệnh) do người lập trình viết với mục đích thực hiện những hành động (Hoặc thông báo) tương ứng với lỗi để chương trình không thực thi sai mục đích.

Việc xử lý lỗi trong PHP khá đơn giản và các bạn sẽ thường thấy hai cách xử lý lỗi sau đây:

- Sử dụng hàm die.
- Sử dụng trường hợp ngoại lệ (Exceptions).

Hàm die có chức năng kết thúc thực thi khối lệnh. Các lập trình viên thường ít khi sử dụng hàm die vào mục đích bắt lỗi vì thường không đáp ứng được yêu cầu lập trình.

Ví dụ:
PHP:
if(!file_exists('path/content.txt')){
die('File not found!');
}else{
unlink('path/content.txt');
}

Trong PHP 5 thì có thêm cách sử lý lỗi chuyên nghiệp hơn đó là dùng ngoại lệ (Exceptions) với cú pháp try..catch. Các bạn có thể sử dụng theo mẫu sau:

try{
//Khối lệnh cần bắt lỗi
}catch(Exception $Biến_exception){
//Xử lý ngoại lệ
}

Ví dụ:
PHP:
try{
if(!file_exists('path/content.txt')){
//Nếu không tìm thấy file thì lưu thông báo lỗi và chuyển sang catch
throw new Exception('File not found!');
}

//Nếu tìm thấy file
unlink('path/content.txt');
}catch(Exception $e){
echo $e->getMessage();
}

Các bạn có thể tìm hiểu thêm về xử lý lỗi ngoại lệ ở liên kết https://php.net/manual/en/language.exceptions.php.

Ngoài ra, trong PHP các bạn có thể tùy biến xuất các thông báo lỗi PHP mà người lập trình không kiểm soát được bằng hàm error_reporting. Các bạn có thể sử dụng hàm error_reporting theo mẫu sau:

error_reporting(Cấp_độ_lỗi);

Ví dụ:
PHP:
error_reporting(E_ALL);
error_reporting(0);

Trong đó, câu lệnh đầu với cấp độ "E_ALL" là hiển thị tất cả các thông báo lỗi còn câu lệnh thứ hai thì ngăn chặn tất cả các thông báo lỗi.

Các bạn có thể tìm hiểu thêm về hàm error_reporting ở liên kết https://php.net/manual/en/function.error-reporting.php.
 
TRANG CHỈNH SỬA THÀNH VIÊN (PHẦN 7)

Nội dung kiến thức phần này gồm:

- Ngày giờ (Date time).

Trong thư mục admin/user, tạo mới file edit.php để viết mã lệnh trang chỉnh sửa thành viên.

Nội dung file admin/user/edit.php như sau:
PHP:
<?php
//Khởi động session
session_start();

//Kiểm tra nếu chưa đăng nhập thì quay về trang đăng nhập
if(!isset($_SESSION['user'])){
header('location:login.php');
}

//Require các file cần thiết
require '../../configs/Config.php';
require '../../models/User.php';

//Lấy user_id từ URL
$user_id = $_GET['user_id'];

//Khởi tạo đối tượng thành viên (User)
$userModel = new User();

//Nếu có POST dữ liệu lên thì xử lý cập nhật
if($_POST){
//Nhận dữ liệu từ form và gán vào một mãng
$data = array(
'username' => $_POST['username'],
'password' => empty($_POST['password']) ? null : md5($_POST['password']),
'fullname' => $_POST['fullname'],
'email' => $_POST['email'],
'status' => isset($_POST['status']) ? 1 : 0,
'modified' => date('Y-m-d H:i:s')
);

//Cập nhật
if($userModel->edit($data, $user_id)){
//Tạo session để lưu cờ thông báo thành công
$_SESSION['success'] = true;

//Tải lại trang (Mục đích là để tải lại thông tin mới)
header('location:edit.php?user_id=' . $user_id);

//Ngừng thực thi
exit();
}
}

//Lấy thông tin thành viên để trình bày trên form
$user = $userModel->getById($user_id);

//Tiêu đề trang
$title = 'Thành viên - Chỉnh sửa';

//View
$view = 'user/edit.tpl.php';

//Require layout
require '../../views/admin/layout.tpl.php';
?>

Mở file models/User.php và thêm vào lớp User khối lệnh mới dưới đây:
PHP:
public function getById($user_id){
//SQL
$sql = "SELECT * FROM tbl_user WHERE user_id = $user_id";

//Query
$this->db->query($sql);

//Fetch
$row = $this->db->fetch();

//Khởi tạo đối tượng UserObj
$userObj = new UserObj();

//Gán thông tin
$userObj->setUserId($row['user_id']);
$userObj->setUsername($row['username']);
$userObj->setPassword($row['password']);
$userObj->setFullname($row['fullname']);
$userObj->setEmail($row['email']);
$userObj->setStatus($row['status']);
$userObj->setCreated($row['created']);
$userObj->setModified($row['modified']);

//Return
return $userObj;
}

public function edit($data, $user_id){
//SQL
$sql = "UPDATE tbl_user SET username = '{$data['username']}', fullname = '{$data['fullname']}', email = '{$data['email']}', status = {$data['status']}, modified = '{$data['modified']}'";

//Nếu có cập nhật mật khẩu
if($data['password'] != null){
$sql .= ", password = '$data['password']'";
}

//Điều kiện
$sql .= " WHERE user_id = $user_id";

//Query và return
return $this->db->query($sql);
}

Trong thư mục views/admin/user, tạo mới file edit.tpl.php để trình bày giao diện trang chỉnh sửa thành viên.

Nội dung file views/admin/user/edit.tpl.php như sau:
HTML:
<form name="edit" method="POST" action="">
<?php if(isset($_SESSION['success'])): ?>
<p style="color:green;">Thành viên đã được chỉnh sửa thành công!</p>
<?php unset($_SESSION['success']); ?>
<?php endif; ?>

<p>
<label>Tài khoản:</label>
<input type="text" name="username" value="<?php echo $user->getUsername(); ?>" />
</p>
<p>
<label>Mật khẩu:</label>
<input type="password" name="password" value="" />
</p>
<p>
<label>Họ tên:</label>
<input type="text" name="fullname" value="<?php echo $user->getFullname(); ?>" />
</p>
<p>
<label>Email:</label>
<input type="text" name="email" value="<?php echo $user->getEmail(); ?>" />
</p>
<p>
<label>Trạng thái:</label>
<input type="checkbox" name="status" value="1" <?php echo ($user->getStatus() == 1) ? 'checked="checked"' : ''; ?> />
</p>
<p>
<input type="submit" value="Chỉnh sửa" />
</p>
</form>

Các bạn truy cập trang danh sách thành viên và nhấp vào tài khoản để truy cập tới trang chỉnh sửa thành viên.

* Ngày giờ (Date time):

Hàm time trong PHP là một hàm "đa năng" về ngày giờ hiện tại giúp các bạn có thể có nhiều thông tin cũng như xử lý ngày giờ hiện tại. Hàm time sẽ trả về cho các bạn một số nguyên đó chính là số giây được tính từ 0 giờ 0 phút 0 giây ngày 1 tháng 1 năm 1970 tới thời điểm bạn gọi hàm.

Có một vài yêu cầu về ngày giờ hoặc do thói quen mà các bạn có thể sử dụng cách lưu trữ ngày giờ trong DB bằng số nguyên giây.

Các bạn có thể tìm hiểu thêm về hàm time ở liên kết https://php.net/manual/en/function.time.php.

Trong PHP, hàm date cũng là một hàm quan trọng để định dạng ngày giờ theo yêu cầu lập trình. Các bạn có thể sử dụng hàm date và hàm strtotime để chuyển đổi kiểu định dạng ngày giờ từ dạng này sang dạng khác một cách linh hoạt và nhanh chóng chứ không cần phải tách ngày giờ rồi ghép lại theo cách thủ công.

Các bạn có thể tìm hiểu thêm về hàm date ở liên kết https://php.net/manual/en/function.date.php và hàm strtotime ở liên kết https://php.net/manual/en/function.strtotime.php.

Ngoài ra, các bạn có thể tìm hiểu thêm về hàm getdate ở liên kết https://php.net/manual/en/function.getdate.php.
 
XÓA THÀNH VIÊN (PHẦN 8)

Phần này tôi không trình bày kiến thức lập trình PHP, các bạn chỉ thực hiện chức năng xóa thành viên dưới đây.

Trong thư mục admin/user, tạo mới file delete.php để viết mã lệnh chức năng xóa thành viên.

Nội dung file admin/user/delete.php như sau:
PHP:
<?php
//Khởi động session
session_start();

//Kiểm tra nếu chưa đăng nhập thì quay về trang đăng nhập
if(!isset($_SESSION['user'])){
header('location:login.php');
}

//Require các file cần thiết
require '../../configs/Config.php';
require '../../models/User.php';

//Lấy user_id từ URL
$user_id = $_GET['user_id'];

//Khởi tạo đối tượng thành viên (User)
$userModel = new User();

//Xóa
$userModel->delete($user_id);

//Quay về trang danh sách thành viên
header('location:list.php');
?>

Mở file models/User.php và thêm vào lớp User khối lệnh mới dưới đây:
PHP:
public function delete($user_id){
//SQL
$sql = "DELETE FROM tbl_user WHERE user_id = $user_id";

//Query và return
return $this->db->query($sql);
}

Xóa là một chức năng nên các bạn không phải tạo giao diện (View).

Các bạn truy cập trang danh sách thành viên, ở cột "Tác vụ" các bạn chọn tác vụ "Xóa" để thực hiện xóa thành viên tương ứng.
 
TRANG DANH SÁCH DANH MỤC SẢN PHẨM (PHẦN 9)

Nội dung kiến thức phần này gồm:

- Hàm tạo (__construct).
- Hàm hủy (__destruct).

Trong thư mục admin, tạo mới thư mục tên là category để chứa các file xử lý liên quan tới danh mục sản phẩm.

Trong thư mục admin/category, tạo mới file list.php để viết mã lệnh trang danh sách danh mục sản phẩm.

Nội dung file admin/category/list.php như sau:
PHP:
<?php
//Khởi động session
session_start();

//Kiểm tra nếu chưa đăng nhập thì quay về trang đăng nhập
if(!isset($_SESSION['user'])){
header('location:login.php');
}

//Require các file cần thiết
require '../../configs/Config.php';
require '../../models/Category.php';

//Khởi tạo đối tượng danh mục sản phẩm (Category)
$categoryModel = new Category();

//Lấy danh sách danh mục sản phẩm
$categoryList = $categoryModel->getList();

//Tiêu đề trang
$title = 'Danh mục sản phẩm - Danh sách';

//View
$view = 'category/list.tpl.php';

//Require layout
require '../../views/admin/layout.tpl.php';
?>

Trong thư mục models các bạn tạo mới hai file CategoryObj.php và Category.php để thể hiện đối tượng danh mục sản phẩm (Lớp CategoryObj) và tương tác với DB (Lớp Category).

Nội dung file models/CategoryObj.php như sau:
PHP:
<?php
class CategoryObj {
protected $categoryId;
protected $name;
protected $status;
protected $created;
protected $modified;

public function setCategoryId($categoryId){
$this->categoryId = $categoryId;
}

public function getCategoryId(){
return $this->categoryId;
}

public function setName($name){
$this->name = $name;
}

public function getName(){
return $this->name;
}

public function setStatus($status){
$this->status = $status;
}

public function getStatus(){
return $this->status;
}

public function setCreated($created){
$this->created = $created;
}

public function getCreated(){
return $this->created;
}

public function setModified($modified){
$this->modified = $modified;
}

public function getModified(){
return $this->modified;
}
}
?>

Nội dung file models/Category.php như sau:
PHP:
<?php
require_once Config::BASE_PATH . 'libraries/Db.php';
require_once Config::BASE_PATH . 'models/CategoryObj.php';

class Category {
protected $db;

public function __construct(){
$this->db = new Db();
}

public function getList(){
//SQL
$sql = "SELECT * FROM tbl_category ORDER BY category_id DESC";

//Query
$this->db->query($sql);

//Tạo mãng lưu trữ
$listCategory = array();

//Fetch
while($row = $this->db->fetch()){
//Khởi tạo đối tượng CategoryObj
$categoryObj = new CategoryObj();

//Gán thông tin
$categoryObj->setCategoryId($row['category_id']);
$categoryObj->setName($row['name']);
$categoryObj->setStatus($row['status']);
$categoryObj->setCreated($row['created']);
$categoryObj->setModified($row['modified']);

//Gán vào mãng lưu trữ
$listCategory[] = $categoryObj;
}

//Return
return $listCategory;
}
}
?>

Trong thư mục views/admin, tạo mới thư mục tên là category để chứa các file trình bày giao diện các trang danh mục sản phẩm.

Trong thư mục views/admin/category, tạo mới file list.tpl.php để trình bày giao diện trang danh sách danh mục sản phẩm.

Nội dung file views/admin/category/list.tpl.php như sau:
HTML:
<table width="100%" cellpadding="10">
<tr>
<th>ID</th>
<th>Tên danh mục</th>
<th>Trạng thái</th>
<th>Ngày tạo</th>
<th>Ngày chỉnh sửa</th>
<th>Tác vụ</th>
</tr>
<?php foreach($categoryList as $category): ?>
<tr>
<td><?php echo $category->getCategoryId(); ?></td>
<td><a href="<?php echo Config::SITE_URL . 'admin/category/edit.php?category_id=' . $category->getCategoryId(); ?>"><?php echo $category->getName(); ?></a></td>
<td><?php echo ($category->getStatus() == 1) ? 'Kích hoạt' : 'Không kích hoạt'; ?></td>
<td><?php echo date('d-m-Y H:i:s', $category->getCreated()); ?></td>
<td><?php echo date('d-m-Y H:i:s', $category->getModified()); ?></td>
<td><a href="<?php echo Config::SITE_URL . 'admin/category/delete.php?category_id=' . $category->getCategoryId(); ?>">Xóa</a></td>
</tr>
<?php endforeach; ?>
</table>

Các bạn truy cập trang danh sách danh mục sản phẩm theo địa chỉ https://localhost/admin/category/list.php để kiểm tra.

* Hàm tạo (__construct):

Hàm tạo là một phương thức đặc biệt, được thực thi ngay khi tạo ra đối tượng. Hàm tạo thường được sử dụng để khởi tạo các chức năng như gán thuộc tính với giá trị hay tạo ra các đối tượng khác từ đối tượng vừa tạo.

Trong PHP, hàm tạo có thể được định nghĩa theo hai cách như sau:

- Đặt tên hàm (Phương thức) trùng tên với tên lớp. Đây là cách được sử dụng ở PHP 4.

Ví dụ:
PHP:
class A {
public function A(){
//Nội dung hàm tạo
}
}

- Dùng hàm __construct (Chỉ PHP 5 mới có).

Ví dụ:
PHP:
class A {
public function __construct(){
//Nội dung hàm tạo
}
}

Trong kế thừa, hàm tạo có một số đặc điểm các bạn cần lưu ý như sau:

- Trường hợp lớp dẫn xuất và lớp cơ sở đều có hàm tạo thì hàm tạo của lớp dẫn xuất sẽ được thực thi, còn hàm tạo của lớp cơ sở không được thực thi.
- Trường hợp lớp dẫn xuất không có hàm tạo còn lớp cơ sở có hàm tạo thì hàm tạo của lớp cơ sở sẽ được thực thi.
- Trường hợp lớp dẫn xuất có hàm tạo còn lớp cơ sở không có hàm tạo thì hàm tạo của lớp dẫn xuất sẽ được thực thi.
- Trường hợp cả lớp dẫn xuất và lớp cơ sở đều không có hàm tạo thì hiển nhiên là không có hàm nào được thực thi khi khởi tạo đối tượng.

* Hàm hủy (__destruct):

Hàm hủy là hàm tự động gọi sau khi đối tượng bị hủy. Hàm hủy thường được sử dụng để giải phóng bộ nhớ chương trình hoặc thực hiện một tác vụ nào đó theo yêu cầu của người lập trình. Lập trình hướng đối tượng trong PHP thì hàm hủy có thể có hoặc không.

Ví dụ:
PHP:
class A {
public function __destruct(){
//Nội dung hàm hủy
}
}

Cách thức hoạt động của hàm hủy trong kế thừa cũng tương tự như hàm tạo.

Các bạn có thể tìm hiểu thêm về hàm tạo và hàm hủy ở liên kết https://php.net/manual/en/language.oop5.decon.php.
 
TRANG THÊM MỚI DANH MỤC SẢN PHẨM (PHẦN 10)

Nội dung kiến thức phần này gồm:

- Autoload (Lazy loading).

Trong thư mục admin/category, tạo mới file add.php để viết mã lệnh trang thêm mới danh mục sản phẩm.

Nội dung file admin/category/add.php như sau:
PHP:
<?php
//Khởi động session
session_start();

//Kiểm tra nếu chưa đăng nhập thì quay về trang đăng nhập
if(!isset($_SESSION['user'])){
header('location:../user/login.php');
}

//Require các file cần thiết
require '../../configs/Config.php';
require '../../models/Category.php';

//Nếu có POST dữ liệu lên thì xử lý
if($_POST){
//Nhận dữ liệu từ form và gán vào một mãng (Có thể sử dụng đối tượng CategoryObj để lưu)
$data = array(
'name' => $_POST['name'],
'status' => isset($_POST['status']) ? 1 : 0,
'created' => date('Y-m-d H:i:s'),
'modified' => date('Y-m-d H:i:s')
);

//Khởi tạo đối tượng danh mục sản phẩm (Category)
$categoryModel = new Category();

//Thêm mới
if($categoryModel->add($data)){
//Tạo session để lưu cờ thông báo thành công
$_SESSION['success'] = true;

//Tải lại trang (Mục đích là để reset form)
header('location:add.php');

//Ngừng thực thi
exit();
}
}

//Tiêu đề trang
$title = 'Danh mục sản phẩm - Thêm mới';

//View
$view = 'category/add.tpl.php';

//Require layout
require '../../views/admin/layout.tpl.php';
?>

Mở file models/Category.php và thêm vào lớp Category khối lệnh mới dưới đây:
PHP:
public function add($data){
//SQL
$sql = "INSERT INTO tbl_category(name, status, created, modified) VALUES('{$data['name']}', {$data['status']}, '{$data['created']}', '{$data['modified']}')";

//Query và return
return $this->db->query($sql);
}

Trong thư mục views/admin/category, tạo mới file add.tpl.php để trình bày giao diện trang thêm mới danh mục sản phẩm.

Nội dung file views/admin/category/add.tpl.php như sau:
HTML:
<form name="add" method="POST" action="">
<?php if(isset($_SESSION['success'])): ?>
<p style="color:green;">Danh mục sản phẩm đã được thêm mới thành công!</p>
<?php unset($_SESSION['success']); ?>
<?php endif; ?>

<p>
<label>Tên danh mục:</label>
<input type="text" name="name" value="" />
</p>
<p>
<label>Trạng thái:</label>
<input type="checkbox" name="status" value="1" />
</p>
<p>
<input type="submit" value="Thêm mới" />
</p>
</form>

Các bạn truy cập trang thêm mới danh mục sản phẩm theo địa chỉ https://localhost/admin/category/add.php để kiểm tra.

* Autoload (Lazy loading):

Trong lập trình hướng đối tượng, đặc biệt là trong mô hình MVC thì autoload là một "thành phần" không thể thiếu.

Autoload sẽ giúp cho việc lập trình trở nên gọn gàng và đơn giản hơn vì nhờ có nó ta có thể tiết kiệm được rất nhiều thao tác gọi nạp file khi sử dụng nhưng cũng tiềm ẩn những bất cập khiến các bạn sẽ gặp khó khăn. Vì vậy, cần phải lưu ý và tìm hiểu thật kỹ càng chức năng này trước khi ứng dụng vào trong dự án của các bạn.

Cụ thể, trong PHP có một hàm "magic" để thực hiện việc này là hàm __autoload. Hàm __autoload sẽ được gọi khi các bạn khởi tạo một đối tượng, PHP sẽ dựa vào tên lớp và khai báo đường dẫn trong hàm __autoload mà tự động require file chứa lớp đó.

Ví dụ:
PHP:
function __autoload($class){
require 'libraries/' . $class . '.php';
}

Với ví dụ trên, khi các bạn khởi tạo một đối tượng từ một lớp bất kỳ nào trong thư mục libraries thì PHP sẽ tự động require file chứa lớp đó.

Vì chức năng tự động này nên về cấu trúc thư mục, cách đặt tên file, tên lớp phải có quy luật (Tôi đã lưu ý ở những phần trước).

Các bạn có thể tìm hiểu thêm về autoload ở liên kết https://php.net/manual/en/language.oop5.autoload.php.
 
TRANG CHỈNH SỬA DANH MỤC SẢN PHẨM (PHẦN 11)

Nội dung kiến thức phần này gồm:

- Override (Ghi đè).

Trong thư mục admin/category, tạo mới file edit.php để viết mã lệnh trang chỉnh sửa danh mục sản phẩm.

Nội dung file admin/category/edit.php như sau:
PHP:
<?php
//Khởi động session
session_start();

//Kiểm tra nếu chưa đăng nhập thì quay về trang đăng nhập
if(!isset($_SESSION['user'])){
header('location:../user/login.php');
}

//Require các file cần thiết
require '../../configs/Config.php';
require '../../models/Category.php';

//Lấy category_id từ URL
$category_id = $_GET['category_id'];

//Khởi tạo đối tượng danh mục sản phẩm (Category)
$categoryModel = new Category();

//Nếu có POST dữ liệu lên thì xử lý cập nhật
if($_POST){
//Nhận dữ liệu từ form và gán vào một mãng
$data = array(
'name' => $_POST['name'],
'status' => isset($_POST['status']) ? 1 : 0,
'modified' => date('Y-m-d H:i:s')
);

//Cập nhật
if($categoryModel->edit($data, $category_id)){
//Tạo session để lưu cờ thông báo thành công
$_SESSION['success'] = true;

//Tải lại trang (Mục đích là để tải lại thông tin mới)
header('location:edit.php?category_id=' . $category_id);

//Ngừng thực thi
exit();
}
}

//Lấy thông tin danh mục sản phẩm để trình bày trên form
$category = $categoryModel->getById($category_id);

//Tiêu đề trang
$title = 'Danh mục sản phẩm - Chỉnh sửa';

//View
$view = 'category/edit.tpl.php';

//Require layout
require '../../views/admin/layout.tpl.php';
?>

Mở file models/Category.php và thêm vào lớp Category khối lệnh mới dưới đây:
PHP:
public function getById($category_id){
//SQL
$sql = "SELECT * FROM tbl_category WHERE category_id = $category_id";

//Query
$this->db->query($sql);

//Fetch
$row = $this->db->fetch();

//Khởi tạo đối tượng CategoryObj
$categoryObj = new CategoryObj();

//Gán thông tin
$categoryObj->setCategoryId($row['category_id']);
$categoryObj->setName($row['name']);
$categoryObj->setStatus($row['status']);
$categoryObj->setCreated($row['created']);
$categoryObj->setModified($row['modified']);

//Return
return $categoryObj;
}

public function edit($data, $category_id){
//SQL
$sql = "UPDATE tbl_category SET name = '{$data['name']}', status = {$data['status']}, modified = '{$data['modified']}' WHERE category_id = $category_id";

//Query và return
return $this->db->query($sql);
}

Trong thư mục views/admin/category, tạo mới file edit.tpl.php để trình bày giao diện trang chỉnh sửa danh mục sản phẩm.

Nội dung file views/admin/category/edit.tpl.php như sau:
HTML:
<form name="edit" method="POST" action="">
<?php if(isset($_SESSION['success'])): ?>
<p style="color:green;">Danh mục sản phẩm đã được chỉnh sửa thành công!</p>
<?php unset($_SESSION['success']); ?>
<?php endif; ?>

<p>
<label>Tên danh mục:</label>
<input type="text" name="name" value="<?php echo $category->getName(); ?>" />
</p>
<p>
<label>Trạng thái:</label>
<input type="checkbox" name="status" value="1" <?php echo ($category->getStatus() == 1) ? 'checked="checked"' : ''; ?> />
</p>
<p>
<input type="submit" value="Chỉnh sửa" />
</p>
</form>

Các bạn truy cập trang danh sách danh mục sản phẩm và nhấp vào tên danh mục để truy cập tới trang chỉnh sửa danh mục sản phẩm.

* Override (Ghi đè):

Trong PHP, nếu như người lập trình tạo ra một phương thức trong lớp dẫn xuất có trùng tên với một phương thức đã được tạo ở lớp cơ sở thì đó được gọi là ghi đè phương thức (Method overriding). Hoặc một cách dễ hiểu hơn như sau:

- Là phương thức đã xuất hiện ở lớp cơ sở và xuất hiện tiếp ở lớp dẫn xuất.
- Khi đối tượng thuộc lớp dẫn xuất gọi phương thức thì sẽ chọn lựa và thực thi theo phương thức trong lớp dẫn xuất. Nếu lớp dẫn xuất không có phương thức đó thì mới lên kiếm ở lớp cơ sở để thực thi.
- Ghi đè là hình thức đa hình (Polymorphism) trong quá trình thực thi (Runtime).

Ví dụ:
PHP:
class A {
public function theMethod(){
echo 'A::theMethod';
}
}

class B extends A {
public function theMethod(){
echo 'B::theMethod';
}
}

$b = new B();
$b->theMethod();

Khi các bạn thực thi khối lệnh trên thì dòng "B::theMethod" sẽ được hiển thị. Nhưng nếu giả sử lớp B không có phương thức theMethod thì dòng "A::theMethod" sẽ được hiển thị.
 
XÓA DANH MỤC SẢN PHẨM (PHẦN 12)

Kể từ phần này tôi sẽ không trình bày kiến thức lập trình PHP nữa. Các bạn thực hiện theo các hướng dẫn của tôi để hoàn thành dự án website giới thiệu sản phẩm.

Trong thư mục admin/category, tạo mới file delete.php để viết mã lệnh chức năng xóa danh mục sản phẩm.

Nội dung file admin/category/delete.php như sau:
PHP:
<?php
//Khởi động session
session_start();

//Kiểm tra nếu chưa đăng nhập thì quay về trang đăng nhập
if(!isset($_SESSION['user'])){
header('location:login.php');
}

//Require các file cần thiết
require '../../configs/Config.php';
require '../../models/Category.php';

//Lấy category_id từ URL
$category_id = $_GET['category_id'];

//Khởi tạo đối tượng danh mục sản phẩm (Category)
$categoryModel = new Category();

//Xóa
$categoryModel->delete($category_id);

//Quay về trang danh sách danh mục sản phẩm
header('location:list.php');
?>
Mở file models/Category.php và thêm vào lớp Category khối lệnh mới dưới đây:
PHP:
public function delete($category_id){
//SQL
$sql = "DELETE FROM tbl_category WHERE category_id = $category_id";

//Query và return
return $this->db->query($sql);
}

Xóa là một chức năng nên các bạn không phải tạo giao diện (View).

Các bạn truy cập trang danh sách danh mục sản phẩm, ở cột "Tác vụ" các bạn chọn tác vụ "Xóa" để thực hiện xóa danh mục sản phẩm tương ứng.
 
TRANG DANH SÁCH SẢN PHẨM (PHẦN 13)

Trong thư mục admin, tạo mới thư mục tên là product để chứa các file xử lý các trang sản phẩm.

Trong thư mục admin/product, tạo mới file list.php để viết mã lệnh trang danh sách sản phẩm.

Nội dung file admin/product/list.php như sau:
PHP:
<?php
//Khởi động session
session_start();

//Kiểm tra nếu chưa đăng nhập thì quay về trang đăng nhập
if(!isset($_SESSION['user'])){
header('location:../user/login.php');
}

//Require các file cần thiết
require '../../configs/Config.php';
require '../../models/Product.php';

//Khởi tạo đối tượng sản phẩm (Product)
$productModel = new Product();

//Lấy danh sách sản phẩm
$productList = $productModel->getList();

//Tiêu đề trang
$title = 'Sản phẩm - Danh sách';

//View
$view = 'product/list.tpl.php';

//Require layout
require '../../views/admin/layout.tpl.php';
?>

Trong thư mục models các bạn tạo mới hai file ProductObj.php và Product.php để thể hiện đối tượng sản phẩm (Lớp ProductObj) và tương tác với DB (Lớp Product).

Nội dung file models/ProductObj.php như sau:
PHP:
<?php
class ProductObj {
protected $productId;
protected $categoryId;
protected $name;
protected $detail;
protected $image;
protected $price;
protected $status;
protected $created;
protected $modified;

public function setProductId($productId){
$this->productId = $productId;
}

public function getProductId(){
return $this->productId;
}

public function setCategoryId($categoryId){
$this->categoryId = $categoryId;
}

public function getCategoryId(){
return $this->categoryId;
}

public function setName($name){
$this->name = $name;
}

public function getName(){
return $this->name;
}

public function setDetail($detail){
$this->detail = $detail;
}

public function getDetail(){
return $this->detail;
}

public function setImage($image){
$this->image = $image;
}

public function getImage(){
return $this->image;
}

public function setPrice($price){
$this->price = $price;
}

public function getPrice(){
return $this->price;
}

public function setStatus($status){
$this->status = $status;
}

public function getStatus(){
return $this->status;
}

public function setCreated($created){
$this->created = $created;
}

public function getCreated(){
return $this->created;
}

public function setModified($modified){
$this->modified = $modified;
}

public function getModified(){
return $this->modified;
}
}
?>

Nội dung file models/Product.php như sau:
PHP:
<?php
require_once Config::BASE_PATH . 'libraries/Db.php';
require_once Config::BASE_PATH . 'models/ProductObj.php';

class Product {
protected $db;

public function __construct(){
$this->db = new Db();
}

public function getList(){
//SQL
$sql = "SELECT * FROM tbl_product ORDER BY product_id DESC";

//Query
$this->db->query($sql);

//Tạo mãng lưu trữ
$listProduct = array();

//Fetch
while($row = $this->db->fetch()){
//Khởi tạo đối tượng ProductObj
$productObj = new ProductObj();

//Gán thông tin
$productObj->setProductId($row['product_id']);
$productObj->setCategoryId($row['category_id']);
$productObj->setName($row['name']);
$productObj->setDetail($row['detail']);
$productObj->setImage($row['image']);
$productObj->setPrice($row['price']);
$productObj->setStatus($row['status']);
$productObj->setCreated($row['created']);
$productObj->setModified($row['modified']);

//Gán vào mãng lưu trữ
$listProduct[] = $productObj;
}

//Return
return $listProduct;
}
}
?>

Trong thư mục views/admin, tạo mới thư mục tên là product để chứa các file trình bày giao diện các trang sản phẩm.

Trong thư mục views/admin/product, tạo mới file list.tpl.php để trình bày giao diện trang danh sách sản phẩm.

Nội dung file views/admin/product/list.tpl.php như sau:
HTML:
<table width="100%" cellpadding="10">
<tr>
<th>ID</th>
<th>Hình ảnh</th>
<th>Tên sản phẩm</th>
<th>Giá bán</th>
<th>Trạng thái</th>
<th>Ngày tạo</th>
<th>Ngày chỉnh sửa</th>
<th>Tác vụ</th>
</tr>
<?php foreach($productList as $product): ?>
<tr>
<td><?php echo $product->getProductId(); ?></td>
<td><img src="<?php echo Config::SITE_URL . 'userfiles/' . $product->getImage(); ?>" width="20" height="20" /></td>
<td><a href="<?php echo Config::SITE_URL . 'admin/product/edit.php?product_id=' . $product->getProductId(); ?>"><?php echo $product->getName(); ?></a></td>
<td><?php echo number_format($product->getPrice(), 0, '', '.'); ?> VNĐ</td>
<td><?php echo ($product->getStatus() == 1) ? 'Kích hoạt' : 'Không kích hoạt'; ?></td>
<td><?php echo date('d-m-Y H:i:s', strtotime($product->getCreated())); ?></td>
<td><?php echo date('d-m-Y H:i:s', strtotime($product->getModified())); ?></td>
<td><a href="<?php echo Config::SITE_URL . 'admin/product/delete.php?product_id=' . $product->getProductId(); ?>">Xóa</a></td>
</tr>
<?php endforeach; ?>
</table>

Các bạn truy cập trang danh sách sản phẩm theo địa chỉ https://localhost/admin/product/list.php để kiểm tra.
 
TRANG THÊM MỚI SẢN PHẨM (PHẦN 14)

Trong thư mục admin/product, tạo mới file add.php để viết mã lệnh trang thêm mới sản phẩm.

Nội dung file admin/product/add.php như sau:
PHP:
<?php
//Khởi động session
session_start();

//Kiểm tra nếu chưa đăng nhập thì quay về trang đăng nhập
if(!isset($_SESSION['user'])){
header('location:../user/login.php');
}

//Require các file cần thiết
require '../../configs/Config.php';
require '../../models/Category.php';
require '../../models/Product.php';

//Nếu có POST dữ liệu lên thì xử lý
if($_POST){
//Upload hình ảnh
if(($image = $_FILES['image']['name']) != null){
move_uploaded_file($_FILES['image']['tmp_name'], 'userfiles/' . $image);
}else{
$image = '';
}

//Nhận dữ liệu từ form và gán vào một mãng (Có thể sử dụng đối tượng ProductObj để lưu)
$data = array(
'category_id' => $_POST['category_id'],
'name' => $_POST['name'],
'detail' => $_POST['detail'],
'image' => $image,
'price' => $_POST['price'],
'status' => isset($_POST['status']) ? 1 : 0,
'created' => date('Y-m-d H:i:s'),
'modified' => date('Y-m-d H:i:s')
);

//Khởi tạo đối tượng sản phẩm (Product)
$productModel = new Product();

//Thêm mới
if($productModel->add($data)){
//Tạo session để lưu cờ thông báo thành công
$_SESSION['success'] = true;

//Tải lại trang (Mục đích là để reset form)
header('location:add.php');

//Ngừng thực thi
exit();
}
}

//Khởi tạo đối tượng danh mục sản phẩm (Category)
$categoryModel = new Category();

//Lấy danh sách danh mục sản phẩm có trạng thái kích hoạt (Status = 1)
$categoryActiveList = $categoryModel->getActiveList();

//Tiêu đề trang
$title = 'Sản phẩm - Thêm mới';

//View
$view = 'product/add.tpl.php';

//Require layout
require '../../views/admin/layout.tpl.php';
?>

Mở file models/Category.php và thêm vào lớp Category khối lệnh mới dưới đây:
PHP:
public function getActiveList(){
//SQL
$sql = "SELECT * FROM tbl_category WHERE status = 1 ORDER BY category_id ASC";

//Query
$this->db->query($sql);

//Tạo mãng lưu trữ
$listCategoryActive = array();

//Fetch
while($row = $this->db->fetch()){
//Khởi tạo đối tượng CategoryObj
$categoryObj = new CategoryObj();

//Gán thông tin
$categoryObj->setCategoryId($row['category_id']);
$categoryObj->setName($row['name']);
$categoryObj->setStatus($row['status']);
$categoryObj->setCreated($row['created']);
$categoryObj->setModified($row['modified']);

//Gán vào mãng lưu trữ
$listCategoryActive[] = $categoryObj;
}

//Return
return $listCategoryActive;
}

Mở file models/Product.php và thêm vào lớp Product khối lệnh mới dưới đây:
PHP:
public function add($data){
//SQL
$sql = "INSERT INTO tbl_product(category_id, name, detail, image, price, status, created, modified) VALUES({$data['category_id']}, '{$data['name']}', '{$data['detail']}', '{$data['image']}', {$data['price']}, {$data['status']}, '{$data['created']}', '{$data['modified']}')";

//Query và return
return $this->db->query($sql);
}

Trong thư mục views/admin/product, tạo mới file add.tpl.php để trình bày giao diện trang thêm mới sản phẩm.

Nội dung file views/admin/product/add.tpl.php như sau:
HTML:
<form name="add" method="POST" enctype="multipart/form-data" action="">
<?php if(isset($_SESSION['success'])): ?>
<p style="color:green;">Sản phẩm đã được thêm mới thành công!</p>
<?php unset($_SESSION['success']); ?>
<?php endif; ?>

<p>
<label>Danh mục:</label>
<select name="category_id">
<?php foreach($categoryActiveList as $categoryActive): ?>
<option value="<?php echo $categoryActive->getCategoryId(); ?>"><?php echo $categoryActive->getName(); ?></option>
<?php endforeach; ?>
</select>
</p>
<p>
<label>Tên sản phẩm:</label>
<input type="text" name="name" value="" />
</p>
<p>
<label>Chi tiết:</label>
<textarea name="detail"></textarea>
</p>
<p>
<label>Hình ảnh:</label>
<input type="file" name="image" />
</p>
<p>
<label>Giá bán:</label>
<input type="text" name="price" value="" />
</p>
<p>
<label>Trạng thái:</label>
<input type="checkbox" name="status" value="1" />
</p>
<p>
<input type="submit" value="Thêm mới" />
</p>
</form>

Các bạn truy cập trang thêm mới sản phẩm theo địa chỉ https://localhost/admin/product/add.php để kiểm tra.
 
TRANG CHỈNH SỬA SẢN PHẨM (PHẦN 15)

Trong thư mục admin/product, tạo mới file edit.php để viết mã lệnh trang chỉnh sửa sản phẩm.

Nội dung file admin/product/edit.php như sau:
PHP:
<?php
//Khởi động session
session_start();

//Kiểm tra nếu chưa đăng nhập thì quay về trang đăng nhập
if(!isset($_SESSION['user'])){
header('location:../user/login.php');
}

//Require các file cần thiết
require '../../configs/Config.php';
require '../../models/Category.php';
require '../../models/Product.php';

//Lấy product_id từ URL
$product_id = $_GET['product_id'];

//Nếu có POST dữ liệu lên thì xử lý cập nhật
if($_POST){
//Upload hình ảnh
if(($image = $_FILES['image']['name']) != null){
move_uploaded_file($_FILES['image']['tmp_name'], 'userfiles/' . $image);
}else{
$image = null;
}

//Nhận dữ liệu từ form và gán vào một mãng
$data = array(
'category_id' => $_POST['category_id'],
'name' => $_POST['name'],
'detail' => $_POST['detail'],
'image' => $image,
'price' => $_POST['price'],
'status' => isset($_POST['status']) ? 1 : 0,
'modified' => date('Y-m-d H:i:s')
);

//Khởi tạo đối tượng sản phẩm (Product)
$productModel = new Product();

//Cập nhật
if($productModel->edit($data, $product_id)){
//Tạo session để lưu cờ thông báo thành công
$_SESSION['success'] = true;

//Tải lại trang (Mục đích là để tải lại thông tin mới)
header('location:edit.php?product_id=' . $product_id);

//Ngừng thực thi
exit();
}
}

//Lấy thông tin sản phẩm để trình bày trên form
$product = $productModel->getById($product_id);

//Khởi tạo đối tượng danh mục sản phẩm (Category)
$categoryModel = new Category();

//Lấy danh sách danh mục sản phẩm có trạng thái kích hoạt (Status = 1)
$categoryActiveList = $categoryModel->getActiveList();

//Tiêu đề trang
$title = 'Sản phẩm - Chỉnh sửa';

//View
$view = 'product/edit.tpl.php';

//Require layout
require '../../views/admin/layout.tpl.php';
?>

Mở file models/Product.php và thêm vào lớp Product khối lệnh mới dưới đây:
PHP:
public function getById($product_id){
//SQL
$sql = "SELECT * FROM tbl_product WHERE product_id = $product_id";

//Query
$this->db->query($sql);

//Fetch
$this->db->fetch()

//Khởi tạo đối tượng ProductObj
$productObj = new ProductObj();

//Gán thông tin
$productObj->setProductId($row['product_id']);
$productObj->setCategoryId($row['category_id']);
$productObj->setName($row['name']);
$productObj->setDetail($row['detail']);
$productObj->setImage($row['image']);
$productObj->setPrice($row['price']);
$productObj->setStatus($row['status']);
$productObj->setCreated($row['created']);
$productObj->setModified($row['modified']);

//Return
return $productObj;
}

public function edit($data, $product_id){
//SQL
$sql = "UPDATE tbl_product SET category_id = {$data['category_id']}, name = '{$data['name']}', detail = '{$data['detail']}', price = {$data['price']}, status = {$data['status']}, modified = '{$data['modified']}'";

//Nếu có cập nhật hình ảnh
if($data['image'] != null){
$sql .= ", image = '{$data['image']}'";
}

//Điều kiện
$sql .= " WHERE product_id = $product_id";

//Query và return
return $this->db->query($sql);
}

Trong thư mục views/admin/product, tạo mới file edit.tpl.php để trình bày giao diện trang chỉnh sửa sản phẩm.

Nội dung file views/admin/product/edit.tpl.php như sau:
HTML:
<form name="edit" method="POST" enctype="multipart/form-data" action="">
<?php if(isset($_SESSION['success'])): ?>
<p style="color:green;">Sản phẩm đã được chỉnh sửa thành công!</p>
<?php unset($_SESSION['success']); ?>
<?php endif; ?>

<p>
<label>Danh mục:</label>
<select name="category_id">
<?php foreach($categoryActiveList as $categoryActive): ?>
<option value="<?php echo $categoryActive->getCategoryId(); ?>" <?php echo ($product->getCategoryId() == $categoryActive->getCategoryId()) ? 'selected="selected"' : ''; ?>><?php echo $categoryActive->getName(); ?></option>
<?php endforeach; ?>
</select>
</p>
<p>
<label>Tên sản phẩm:</label>
<input type="text" name="name" value="<?php echo $product->getName(); ?>" />
</p>
<p>
<label>Chi tiết:</label>
<textarea name="detail"><?php echo $product->getDetail(); ?></textarea>
</p>
<p>
<label>Hình ảnh:</label>
<input type="file" name="image" />
</p>
<p>
<label>Giá bán:</label>
<input type="text" name="price" value="<?php echo $product->getPrice(); ?>" />
</p>
<p>
<label>Trạng thái:</label>
<input type="checkbox" name="status" value="1" <?php echo ($product->getStatus() == 1) ? 'checked="checked"' : ''; ?> />
</p>
<p>
<input type="submit" value="Chỉnh sửa" />
</p>
</form>

Các bạn truy cập trang danh sách sản phẩm và nhấp vào tên sản phẩm để truy cập tới trang chỉnh sửa sản phẩm.
 
XÓA SẢN PHẨM (PHẦN 16)

Trong thư mục admin/product, tạo mới file delete.php để viết mã lệnh chức năng xóa sản phẩm.

Nội dung file admin/product/delete.php như sau:
PHP:
<?php
//Khởi động session
session_start();

//Kiểm tra nếu chưa đăng nhập thì quay về trang đăng nhập
if(!isset($_SESSION['user'])){
header('location:../user/login.php');
}

//Require các file cần thiết
require '../../configs/Config.php';
require '../../models/Product.php';

//Lấy product_id từ URL
$product_id = $_GET['product_id'];

//Khởi tạo đối tượng sản phẩm (Product)
$productModel = new Product();

//Xóa
$productModel->delete($product_id);

//Quay về trang danh sách sản phẩm
header('location:list.php');
?>

Mở file models/Product.php và thêm vào lớp Product khối lệnh mới dưới đây:
PHP:
public function delete($product_id){
//SQL
$sql = "DELETE FROM tbl_product WHERE product_id = $product_id";

//Query và return
return $this->db->query($sql);
}

Xóa là một chức năng nên các bạn không phải tạo giao diện (View).

Các bạn truy cập trang danh sách sản phẩm, ở cột "Tác vụ" các bạn chọn tác vụ "Xóa" để thực hiện xóa sản phẩm tương ứng.
 
MENU (PHẦN 17)

Các trang trong phần quản trị đã được tôi hướng dẫn ở các phần trước. Phần này tôi sẽ hướng dẫn các bạn tạo ra một menu để truy cập tới các trang. Menu này chỉ là các mã lệnh HTML chứ không có gì đặc biệt nên tôi sẽ trình bày ngắn gọn.

Mở file views/admin/layout.tpl.php và thêm vào sau thẻ <body> khối lệnh HTML sau:
PHP:
<div>
<ul style="float:left;">
<li>
<a href="<?php echo SITE_URL . 'admin'; ?>">Trang chủ</a>
</li>
</ul>
<ul style="float:left;">
<li>
<a href="<?php echo SITE_URL . 'admin/category/list.php'; ?>">Danh sách danh mục sản phẩm</a>
</li>
<li>
<a href="<?php echo SITE_URL . 'admin/category/add.php'; ?>">Thêm mới danh mục sản phẩm</a>
</li>
</ul>
<ul style="float:left;">
<li>
<a href="<?php echo SITE_URL . 'admin/product/list.php'; ?>">Danh sách sản phẩm</a>
</li>
<li>
<a href="<?php echo SITE_URL . 'admin/product/add.php'; ?>">Thêm mới sản phẩm</a>
</li>
</ul>
<ul style="float:left;">
<li>
<a href="<?php echo SITE_URL . 'admin/user/list.php'; ?>">Danh sách thành viên</a>
</li>
<li>
<a href="<?php echo SITE_URL . 'admin/user/add.php'; ?>">Thêm mới thành viên</a>
</li>
</ul>

<div style="clear:both;"></div>
</div>

Đây là phần cuối cùng trong phần quản trị (Admin). Kể từ phần sau tôi sẽ hướng dẫn các bạn thực hiện các trang trong phần người dùng (Front).
 
TRANG CHỦ (PHẦN 18)

Kể từ phần này tôi sẽ hướng dẫn các bạn thực hiện các trang người dùng (Front).

Tại thư mục gốc (Root), các bạn tạo file index.php để viết mã lệnh xử lý trang chủ.

Nội dung file index.php như sau:
PHP:
<?php
//Require các file cần thiết
require 'configs/Config.php';
require 'models/Product.php';

//Khởi tạo đối tượng sản phẩm (Product)
$productModel = new Product();

//Lấy danh sách 6 sản phẩm mới nhất
$productLatestList = $productModel->getLatestList();

//Tiêu đề trang
$title = 'Trang chủ';

//View
$view = 'home/home.tpl.php';

//Require layout
require 'views/front/layout.tpl.php';
?>

Mở file models/Product.php và thêm vào lớp Product khối lệnh mới dưới đây:
PHP:
public function getLatestList($limit = 6){
//SQL
$sql = "SELECT * FROM tbl_product ORDER BY product_id DESC LIMIT 0, $limit";

//Query
$this->db->query($sql);

//Tạo mãng lưu trữ
$listProductLatest = array();

//Fetch
while($row = $this->db->fetch()){
//Khởi tạo đối tượng ProductObj
$productObj = new ProductObj();

//Gán thông tin
$productObj->setProductId($row['product_id']);
$productObj->setCategoryId($row['category_id']);
$productObj->setName($row['name']);
$productObj->setDetail($row['detail']);
$productObj->setImage($row['image']);
$productObj->setPrice($row['price']);
$productObj->setStatus($row['status']);
$productObj->setCreated($row['created']);
$productObj->setModified($row['modified']);

//Gán vào mãng lưu trữ
$listProductLatest[] = $productObj;
}

//Return
return $listProductLatest;
}

Trong thư mục views/front, tạo mới file layout.tpl.php để trình bày layout của website.

Nội dung file views/front/layout.tpl.php như sau:
Mã:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Website giới thiệu sản phẩm - <?php echo $title; ?></title>
</head>
<body>

<?php require Config::BASE_PATH . 'views/front/' . $view; ?>

</body>
</html>

Trong thư mục views/front, tạo mới thư mục tên là home để chứa các file trình bày giao diện trang chủ.

Trong thư mục views/front/home, tạo mới file home.tpl.php để trình bày giao diện trang chủ.

Nội dung file views/front/home/home.tpl.php như sau:
Mã:
<?php foreach($productLatestList as $productLatest): ?>
<div style="padding:10px;">
<p>
<img src="<?php echo Config::SITE_URL . 'userfiles/' . $productLatest->getImage(); ?>" />
</p>
<h4>
<a href="<?php echo Config::SITE_URL . 'product-detail.php?product_id=' . $productLatest->getProductId(); ?>"><?php echo $productLatest->getName(); ?></a>
</h4>
<p><?php echo number_format($productLatest->getPrice(), 0, '', '.'); ?> VNĐ</p>
</div>
<?php endforeach; ?>

Các bạn truy cập trang chủ theo địa chỉ https://localhost để kiểm tra.
 
TRANG DANH SÁCH SẢN PHẨM (PHẦN 19)

Tại thư mục gốc (Root), các bạn tạo mới file product-list.php để viết mã lệnh xử lý trang danh sách sản phẩm.

Nội dung file product-list.php như sau:
PHP:
<?php
//Require các file cần thiết
require 'configs/Config.php';
require 'models/Product.php';

//Lấy category_id từ URL (Nếu có)
$category_id = isset($_GET['category_id']) ? ((int)$_GET['category_id']) : null;

//Khởi tạo đối tượng sản phẩm (Product)
$productModel = new Product();

//Lấy danh sách sản phẩm
$productActiveList = $productModel->getActiveList($category_id);

//Tiêu đề trang
$title = 'Sản phẩm';

//View
$view = 'product/list.tpl.php';

//Require layout
require 'views/front/layout.tpl.php';
?>

Mở file models/Product.php và thêm vào lớp Product khối lệnh mới dưới đây:
PHP:
public function getActiveList($category_id){
//SQL
$sql = "SELECT * FROM tbl_product WHERE status = 1";

//Nếu lấy sản phẩm theo danh mục
if($category_id != null){
$sql .= " AND category_id = $category_id";
}

//Sắp xếp
$sql .= " ORDER BY product_id DESC";

//Query
$this->db->query($sql);

//Tạo mãng lưu trữ
$listProductActive = array();

//Fetch
while($row = $this->db->fetch()){
//Khởi tạo đối tượng ProductObj
$productObj = new ProductObj();

//Gán thông tin
$productObj->setProductId($row['product_id']);
$productObj->setCategoryId($row['category_id']);
$productObj->setName($row['name']);
$productObj->setDetail($row['detail']);
$productObj->setImage($row['image']);
$productObj->setPrice($row['price']);
$productObj->setStatus($row['status']);
$productObj->setCreated($row['created']);
$productObj->setModified($row['modified']);

//Gán vào mãng lưu trữ
$listProductActive[] = $productObj;
}

//Return
return $listProductActive;
}

Trong thư mục views/front, tạo mới thư mục tên là product để chứa các file trình bày giao diện các trang sản phẩm.

Trong thư mục views/front/product, tạo mới file list.tpl.php để trình bày giao diện trang danh sách sản phẩm.

Nội dung file views/front/product/list.tpl.php như sau:
Mã:
<?php foreach($productActiveList as $productActive): ?>
<div style="padding:10px;">
<p>
<img src="<?php echo Config::SITE_URL . 'userfiles/' . $productActive->getImage(); ?>" />
</p>
<h4>
<a href="<?php echo Config::SITE_URL . 'product-detail.php?product_id=' . $productActive->getProductId(); ?>"><?php echo $productActive->getName(); ?></a>
</h4>
<p><?php echo number_format($productActive->getPrice(), 0, '', '.'); ?> VNĐ</p>
</div>
<?php endforeach; ?>

Các bạn truy cập trang danh sách sản phẩm theo địa chỉ https://localhost/product-list.php để kiểm tra.
 
TRANG CHI TIẾT SẢN PHẨM (PHẦN 20)

Tại thư mục gốc (Root), các bạn tạo mới file product-detail.php để viết mã lệnh xử lý trang chi tiết sản phẩm.

Nội dung file product-detail.php như sau:
PHP:
<?php
//Require các file cần thiết
require 'configs/Config.php';
require 'models/Product.php';

//Lấy product_id từ URL
$product_id = isset($_GET['product_id']) ? ((int)$_GET['product_id']) : 0;

//Khởi tạo đối tượng sản phẩm (Product)
$productModel = new Product();

//Lấy thông tin sản phẩm
$productActive = $productModel->getActiveById($product_id);

//Tiêu đề trang
$title = 'Chi tiết sản phẩm';

//View
$view = 'product/detail.tpl.php';

//Require layout
require 'views/front/layout.tpl.php';
?>

Mở file models/Product.php và thêm vào lớp Product khối lệnh mới dưới đây:
PHP:
public function getActiveById($product_id){
//SQL
$sql = "SELECT * FROM tbl_product WHERE status = 1 AND product_id = $product_id";

//Query
$this->db->query($sql);

//Fetch
$this->db->fetch()

//Khởi tạo đối tượng ProductObj
$productObj = new ProductObj();

//Gán thông tin
$productObj->setProductId($row['product_id']);
$productObj->setCategoryId($row['category_id']);
$productObj->setName($row['name']);
$productObj->setDetail($row['detail']);
$productObj->setImage($row['image']);
$productObj->setPrice($row['price']);
$productObj->setStatus($row['status']);
$productObj->setCreated($row['created']);
$productObj->setModified($row['modified']);

//Return
return $productObj;
}

Trong thư mục views/front/product, tạo mới file detail.tpl.php để trình bày giao diện trang chi tiết sản phẩm.

Nội dung file views/front/product/detail.tpl.php như sau:
Mã:
<div style="padding:10px;">
<p>
<img src="<?php echo Config::SITE_URL . 'userfiles/' . $productActive->getImage(); ?>" />
</p>
<h4><?php echo $productActive->getName(); ?></h4>
<p><?php echo number_format($productActive->getPrice(), 0, '', '.'); ?> VNĐ</p>
</div>

Các bạn truy cập trang danh sách sản phẩm, sau đó nhấp vào tên sản phẩm để truy cập trang chi tiết sản phẩm.
 
×
Quay lại
Top