Advance 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

Language
Tiếng Việt
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 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.

Chân thành cảm ơn sự quan tâm của các bạn!
 
PHẦN 1: THÔNG TIN CẤU HÌNH WEBSITE

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 (Constant).
Trong thư mục configs, tạo mới tập tin Config.php để khai báo các thông tin cấu hình website. Bạn lưu ý tập tin Config.php tôi viết hoa chữ "C". Chuẩn đặt tên tập tin, 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 tập tin configs/Config.php như sau:
PHP:
<?php
class Config
{
    //Thông tin website
    const SITE_URL = 'http://localhost/';
    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 = 'training_php_02';
}
?>
1. 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:
Code:
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 tập tin PHP (Vì vậy tên tập tin 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 tại địa chỉ http://php.net/manual/en/language.oop5.php.

2. Hằng (Constant):

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.

Tập tin 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:
Code:
const Tên_hằng = Giá_trị_của_hằng;
Ví dụ:
PHP:
const SITE_URL = 'http://localhost/';
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:
Code:
Tên_lớp::Tên_hằng;
Ví dụ:
PHP:
Config::SITE_URL;
Các bạn có thể tìm hiểu thêm về hằng tại địa chỉ http://php.net/manual/en/language.oop5.constants.php.
 
PHẦN 2: KẾT NỐI CƠ SỞ DỮ LIỆU

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, các bạn tạo mới 2 tập tin MySql.php và Db.php để viết mã lệnh kết nối DB.

Nội dung tập tin libraries/MySql.php như sau:
PHP:
<?php
class MySql
{
    protected $connection;
    protected $query;
   
    public function __construct()
    {
        //Kết nối DB
        $this->connection = mysqli_connect(Config::DB_SERVER, Config::DB_USERNAME, Config::DB_PASSWORD, Config::DB_DATABASE) or die('Not connected DB!');
       
        //Yêu cầu lưu trữ UTF8 (Tiếng Việt)
        mysqli_query('SET NAMES UTF8', $this->connection);
    }
   
    public function query($sql)
    {
        //Truy vấn
        return $this->query = mysqli_query($this->connection, $sql);
    }
   
    public function fetch()
    {
        //Lấy dữ liệu
        return mysqli_fetch_assoc($this->query);
    }
}
?>
Nội dung tập tin libraries/Db.php như sau:
PHP:
<?php
require Config::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 tập tin libraries/MySql.php. Vậy lớp Db được viết trong tập tin 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à các bạn nên tìm hiểu thêm.

Các bạn lưu ý tới 2 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).
1. 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:
Code:
Phạm_vi $Tên_thuộc_tính;
Hoặc
Code:
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 3 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 hay 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) như sau:
  • 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ừ 2 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).
Để 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:
Code:
$this->Tên_thuộc_tính;
Hoặc
Code:
self::$Tên_thuộc_tính;
Hoặc
Code:
parent::$Tên_thuộc_tính;
Ví dụ:
PHP:
$this->a;
self::$b;
parent::$c;
Với 3 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. Các bạn có thể sử dụng như sau:
  • 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 tại địa chỉ http://php.net/manual/en/language.oop5.properties.php.

2. 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:
Code:
Phạm_vi function Tên_phương_thức()
{
    //Nội dung phương thức
}
Hoặc
Code:
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:
Code:
$this->Tên_phương_thức();
Hoặc
Code:
self::Tên_phương_thức();
Hoặc
Code:
parent::Tên_phương_thức();
Ví dụ:
PHP:
$this->query('SELECT * FROM tbl_user');
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 tại địa chỉ http://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.

3. 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:
Code:
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) tại địa chỉ http://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.
 
PHẦN 3: TRANG ĐĂNG NHẬP, ĐĂNG XUẤT

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 tập tin xử lý liên quan tới người dùng (Đă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 tập tin login.php để viết mã lệnh xử lý đăng nhập.

Nội dung tập tin 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 tập tin 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 người dùng (User)
    $userModel = new User();
    
    //Lấy thông tin người dùng
    $user = $userModel->getByUsername($username);
    
    //Kiểm tra sự tồn tại của người dùng và mật khẩu có trùng khớp
    if ($user && $user->getPassword() === md5($password)) {
        //Tạo session lưu thông tin người dùng đă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 tập tin giao diện (View)
require '../../views/admin/user/login.tpl.php';
?>
Trong thư mục models các bạn tạo mới 2 tập tin UserObj.php và User.php để thể hiện đối tượng người dùng (Lớp UserObj) và tương tác với DB (Lớp User).

Nội dung tập tin 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 tập tin 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 tập tin trình bày giao diện HTML trang người dùng.

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

Nội dung tập tin views/admin/user/login.tpl.php như sau:
PHP:
<!DOCTYPE html>
<html>
<head>
<meta 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), trạng thái (Nhập giá trị là 1)... rồi bắt đầu truy cập trang đăng nhập theo địa chỉ http://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 tập tin logout.php để xử lý đăng xuất.

Nội dung tập tin 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á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:
Code:
$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:
Code:
$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 tại địa chỉ http://php.net/manual/en/language.types.object.php.
 
PHẦN 4: TRANG DANH SÁCH NGƯỜI DÙNG

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 tập tin list.php để viết mã lệnh trang danh sách người dùng.

Nội dung tập tin 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 tập tin cần thiết
require '../../configs/Config.php';
require '../../models/User.php';

//Khởi tạo đối tượng người dùng (User)
$userModel = new User();

//Lấy danh sách người dùng
$userList = $userModel->getList();

//Tiêu đề trang
$title = 'Người dùng - Danh sách';

//Giao diện (View)
$view = 'user/list.tpl.php';

//Require khung giao diện (Layout)
require '../../views/admin/layout.tpl.php';
?>
Mở tập tin 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 khung giao diện (Layout) của website. Khung giao diện 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 khung giao diện 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 tập tin layout.tpl.php để trình bày khung giao diện của website.

Nội dung tập tin views/admin/layout.tpl.php như sau:
PHP:
<!DOCTYPE html>
<html>
<head>
<meta 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 tập tin list.tpl.php để trình bày giao diện trang danh sách người dùng.

Nội dung tập tin views/admin/user/list.tpl.php như sau:
PHP:
<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', strtotime($user->getCreated())); ?>
            </td>
            <td>
                <?php echo date('d/m/Y H:i:s', strtotime($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 người dùng theo địa chỉ http://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 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ó. 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 chính quy. 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 nó.

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 +, *, ?, {} 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 bao gồm 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 tại địa chỉ http://php.net/manual/en/ref.regex.phphttp://php.net/manual/en/ref.pcre.php.
 
PHẦN 5: TRANG THÊM MỚI NGƯỜI DÙNG

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 tập tin add.php để viết mã lệnh trang thêm mới người dùng.

Nội dung tập tin 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 tập tin 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 người dùng (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 = 'Người dùng - Thêm mới';

//Giao diện (View)
$view = 'user/add.tpl.php';

//Require khung giao diện (Layout)
require '../../views/admin/layout.tpl.php';
?>
Mở tập tin 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']}')";

    //Return
    return $this->db->query($sql);
}
Trong thư mục views/admin/user, tạo mới tập tin add.tpl.php để trình bày giao diện trang thêm mới người dùng.

Nội dung tập tin views/admin/user/add.tpl.php như sau:
PHP:
<form name="add" method="post" action="">
    <?php if (isset($_SESSION['success'])): ?>
        <p style="color: green;">Người dùng đã đượ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 người dùng theo địa chỉ http://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 2 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');
}
Kể từ PHP 5 trở đi thì có thêm cách xử 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:
Code:
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 tập tin 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 tập tin
    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ệ tại địa chỉ http://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:
Code:
error_reporting(Cấp_độ_báo_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 tại địa chỉ http://php.net/manual/en/function.error-reporting.php.
 
PHẦN 6: TRANG CHỈNH SỬA NGƯỜI DÙNG

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 tập tin edit.php để viết mã lệnh trang chỉnh sửa người dùng.

Nội dung tập tin 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 tập tin 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 người dùng (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 người dùng để trình bày trên form
$user = $userModel->getById($user_id);

//Tiêu đề trang
$title = 'Người dùng - Chỉnh sửa';

//Giao diện (View)
$view = 'user/edit.tpl.php';

//Require khung giao diện (Layout)
require '../../views/admin/layout.tpl.php';
?>
Mở tập tin 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";

    //Return
    return $this->db->query($sql);
}
Trong thư mục views/admin/user, tạo mới tập tin edit.tpl.php để trình bày giao diện trang chỉnh sửa người dùng.

Nội dung tập tin views/admin/user/edit.tpl.php như sau:
PHP:
<form name="edit" method="post" action="">
    <?php if (isset($_SESSION['success'])): ?>
        <p style="color: green;">Người dùng đã đượ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 người dùng và nhấp vào tài khoản để truy cập tới trang chỉnh sửa người dùng.

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 01 tháng 01 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 tại địa chỉ http://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 tại địa chỉ http://php.net/manual/en/function.date.php và hàm strtotime tại địa chỉ http://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 tại địa chỉ http://php.net/manual/en/function.getdate.php.
 
PHẦN 7: XOÁ NGƯỜI DÙNG

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 người dùng dưới đây.

Trong thư mục admin/user, tạo mới tập tin delete.php để viết mã lệnh chức năng xóa người dùng.

Nội dung tập tin 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 tập tin 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 người dùng (User)
$userModel = new User();

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

//Quay về trang danh sách người dùng
header('location:list.php');
?>
Mở tập tin 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";

    //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 người dùng, ở cột "Tác vụ" các bạn chọn tác vụ "Xóa" để thực hiện xóa người dùng tương ứng.
 
PHẦN 8: TRANG DANH SÁCH DANH MỤC SẢN PHẨM

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 tập tin xử lý liên quan tới danh mục sản phẩm.

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

Nội dung tập tin 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 tập tin 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';

//Giao diện (View)
$view = 'category/list.tpl.php';

//Require khung giao diện (Layout)
require '../../views/admin/layout.tpl.php';
?>
Trong thư mục models các bạn tạo mới 2 tập tin 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 tập tin 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 tập tin 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 tập tin 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 tập tin list.tpl.php để trình bày giao diện trang danh sách danh mục sản phẩm.

Nội dung tập tin views/admin/category/list.tpl.php như sau:
PHP:
<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', strtotime($category->getCreated())); ?>
            </td>
            <td>
                <?php echo date('d/m/Y H:i:s', strtotime($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ỉ http://localhost/admin/category/list.php để kiểm tra.

1. 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 2 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 (Từ PHP 5 trở đi).

Ví dụ:
PHP:
class A
{
    public function __construct()
    {
        //Nội dung hàm tạo
    }
}
Phạm vi của hàm tạo luôn luôn là public.

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.
2. 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
    }
}
Phạm vi của hàm hủy luôn luôn là public.

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 tại địa chỉ http://php.net/manual/en/language.oop5.decon.php.
 
PHẦN 9: TRANG THÊM MỚI DANH MỤC SẢN PHẨM

Nội dung kiến thức phần này gồm:
  • Nạp tự động (Autoload).
Trong thư mục admin/category, tạo mới tập tin add.php để viết mã lệnh trang thêm mới danh mục sản phẩm.

Nội dung tập tin 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 tập tin 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';

//Giao diện (View)
$view = 'category/add.tpl.php';

//Require khung giao diện (Layout)
require '../../views/admin/layout.tpl.php';
?>
Mở tập tin 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']}')";

    //Return
    return $this->db->query($sql);
}
Trong thư mục views/admin/category, tạo mới tập tin 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 tập tin views/admin/category/add.tpl.php như sau:
PHP:
<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ỉ http://localhost/admin/category/add.php để kiểm tra.

Nạp tự động (Autoload):

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

Nạp tự động 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 tập tin (Require) 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ỹ chức năng này trước khi áp 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 nạp tập tin 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 nạp tập tin 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 tập tin, 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ề nạp tự động tại địa chỉ http://php.net/manual/en/language.oop5.autoload.php.
 
PHẦN 10: TRANG CHỈNH SỬA DANH MỤC SẢN PHẨM

Nội dung kiến thức phần này gồm:
  • Ghi đè (Override).
Trong thư mục admin/category, tạo mới tập tin edit.php để viết mã lệnh trang chỉnh sửa danh mục sản phẩm.

Nội dung tập tin 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 tập tin 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';

//Giao diện (View)
$view = 'category/edit.tpl.php';

//Require khung giao diện (Layout)
require '../../views/admin/layout.tpl.php';
?>
Mở tập tin 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";

    //Return
    return $this->db->query($sql);
}
Trong thư mục views/admin/category, tạo mới tập tin 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 tập tin views/admin/category/edit.tpl.php như sau:
PHP:
<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.

Ghi đè (Override):

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 tìm 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ị.
 
PHẦN 11: XOÁ DANH MỤC SẢN PHẨM

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 tập tin delete.php để viết mã lệnh chức năng xóa danh mục sản phẩm.

Nội dung tập tin 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 tập tin 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ở tập tin 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";

    //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.
 
PHẦN 12: TRANG DANH SÁCH SẢN PHẨM

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

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

Nội dung tập tin 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 tập tin 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';

//Giao diện (View)
$view = 'product/list.tpl.php';

//Require khung giao diện (Layout)
require '../../views/admin/layout.tpl.php';
?>
Trong thư mục models các bạn tạo mới 2 tập tin 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 tập tin 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 tập tin 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 tập tin 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 tập tin list.tpl.php để trình bày giao diện trang danh sách sản phẩm.

Nội dung tập tin views/admin/product/list.tpl.php như sau:
PHP:
<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="30" height="30">
            </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ỉ http://localhost/admin/product/list.php để kiểm tra.
 
PHẦN 13: TRANG THÊM MỚI SẢN PHẨM

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

Nội dung tập tin 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 tập tin 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) {
    //Tải lên hình ảnh (Upload)
    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';

//Giao diện (View)
$view = 'product/add.tpl.php';

//Require khung giao diện (Layout)
require '../../views/admin/layout.tpl.php';
?>
Mở tập tin 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ở tập tin 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']}')";

    //Return
    return $this->db->query($sql);
}
Trong thư mục views/admin/product, tạo mới tập tin add.tpl.php để trình bày giao diện trang thêm mới sản phẩm.

Nội dung tập tin views/admin/product/add.tpl.php như sau:
PHP:
<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ỉ http://localhost/admin/product/add.php để kiểm tra.
 
PHẦN 14: TRANG CHỈNH SỬA SẢN PHẨM

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

Nội dung tập tin 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 tập tin 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) {
    //Tải lên hình ảnh (Upload)
    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';

//Giao diện (View)
$view = 'product/edit.tpl.php';

//Require khung giao diện (Layout)
require '../../views/admin/layout.tpl.php';
?>
Mở tập tin 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";

    //Return
    return $this->db->query($sql);
}
Trong thư mục views/admin/product, tạo mới tập tin edit.tpl.php để trình bày giao diện trang chỉnh sửa sản phẩm.

Nội dung tập tin views/admin/product/edit.tpl.php như sau:
PHP:
<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 ($categoryActive->getCategoryId() == $product->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.
 
PHẦN 15: XOÁ SẢN PHẨM

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

Nội dung tập tin 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 tập tin 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ở tập tin 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";

    //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.
 
PHẦN 16: TRÌNH ĐƠN QUẢN TRỊ

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 trình đơn để truy cập tới các trang. Trình đơn 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ở tập tin views/admin/layout.tpl.php và thêm vào sau thẻ body khối lệnh sau:
PHP:
<div>
    <ul style="float: left;">
        <li>
            <a href="<?php echo Config::SITE_URL . 'admin'; ?>">Trang chủ</a>
        </li>
    </ul>
    <ul style="float: left;">
        <li>
            <a href="<?php echo Config::SITE_URL . 'admin/category/list.php'; ?>">Danh sách danh mục sản phẩm</a>
        </li>
        <li>
            <a href="<?php echo Config::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 Config::SITE_URL . 'admin/product/list.php'; ?>">Danh sách sản phẩm</a>
        </li>
        <li>
            <a href="<?php echo Config::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 Config::SITE_URL . 'admin/user/list.php'; ?>">Danh sách người dùng</a>
        </li>
        <li>
            <a href="<?php echo Config::SITE_URL . 'admin/user/add.php'; ?>">Thêm mới người dùng</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).
 
PHẦN 17: TRANG CHỦ

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 tập tin index.php để viết mã lệnh xử lý trang chủ.

Nội dung tập tin index.php như sau:
PHP:
<?php
//Require các tập tin 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ủ';

//Giao diện (View)
$view = 'home/home.tpl.php';

//Require khung giao diện (Layout)
require 'views/front/layout.tpl.php';
?>
Mở tập tin 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 tập tin layout.tpl.php để trình bày khung giao diện của website.

Nội dung tập tin views/front/layout.tpl.php như sau:
PHP:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title><?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 tập tin trình bày giao diện trang chủ.

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

Nội dung tập tin views/front/home/home.tpl.php như sau:
PHP:
<?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ỉ http://localhost để kiểm tra.
 
PHẦN 18: TRANG DANH SÁCH SẢN PHẨM

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

Nội dung tập tin product-list.php như sau:
PHP:
<?php
//Require các tập tin 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']) ? $_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';

//Giao diện (View)
$view = 'product/list.tpl.php';

//Require khung giao diện (Layout)
require 'views/front/layout.tpl.php';
?>
Mở tập tin 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 tập tin 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 tập tin list.tpl.php để trình bày giao diện trang danh sách sản phẩm.

Nội dung tập tin views/front/product/list.tpl.php như sau:
PHP:
<?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ỉ http://localhost/product-list.php để kiểm tra.
 
PHẦN 19: TRANG CHI TIẾT SẢN PHẨM

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

Nội dung tập tin product-detail.php như sau:
PHP:
<?php
//Require các tập tin 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();

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

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

//Giao diện (View)
$view = 'product/detail.tpl.php';

//Require khung giao diện (Layout)
require 'views/front/layout.tpl.php';
?>
Mở tập tin 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 tập tin detail.tpl.php để trình bày giao diện trang chi tiết sản phẩm.

Nội dung tập tin views/front/product/detail.tpl.php như sau:
PHP:
<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.
 
Back
Top