PHP TUTORIALS
Hướng dẫn tạo domain ảo tại localhost với XAMPP trên Window Có gì mới trong PHP 8 (Tính năng, Cải tiến và Trình biên dịch JIT) Hướng dẫn viết ứng dụng kiểm tra năm sinh theo âm lịch bằng PHP Xử lý realtime trong PHP sử dụng pusher Hướng dẫn gửi mail trong PHP với PHPMailer Bóc tách dữ liệu từ trang khác bằng PHP Simple HTML DOM Parser Cách sửa lỗi hình ảnh khi đăng bài viết lên Facebook Hướng dẫn tạo thông báo realtime với pushcrew Kích thước chuẩn và cách làm FavIcon icon Tìm hiểu bản chất vòng lặp foreach trong php Những vấn đề nâng cao kỹ năng lập trình trong php Sử Dụng Vòng Lặp Xuất Dữ Liệu Bảng Tính Lương Tìm hiểu thuật toán phân trang trong php Giới hạn số trang trong thuật toán phân trang Bài 01: Đệ quy menu đa cấp với php và mysql - phần 1 Bài 02: Đệ quy menu đa cấp với php và mysql - phần 2 Tích hợp bộ search google vào website Nên dùng count() hay sizeof() để đếm số phần tử của mảng Bài 01: Lấy dữ liệu từ mysql lưu vào file excel với PHPExcel Tạo slug tự động bằng JavaScript và PHP RSS là gì? Cách Tạo RSS cho Website PHP CMS là gì? Các CMS phổ biến hiện nay (update 2024) Web động là gì? Web tĩnh là gì? Chặn referrer từ website simple-share-buttons.com Bảng mã ASCII chuẩn các hệ nhị phân - thập phân - thập lục phân Hướng dẫn tạo Facebook App để lấy App ID và Secret Key Download Facebook SDK cho PHP Mối liên hệ giữa HTML - PHP - MYSQL Tự động post bài viết lên tường với hootsuite.com Kiểm tra người dùng đã đăng nhập hay chưa bằng PHP Lấy video kênh Youtube mới nhất bằng jQuery và Youtube API V3 Đệ quy chuyên mục đa cấp trong PHP toàn tập Font Awesome là gì? Cách sử dụng Font Awesome Tích hợp đăng nhập Google vào Website Thuật toán phân trang với PHP và MySQL Cài đặt LAMP trên Fedora
CÁC CHỦ ĐỀ
BÀI MỚI NHẤT
MỚI CẬP NHẬT

Đệ quy chuyên mục đa cấp trong PHP toàn tập

Sẵn có câu hỏi trên trang qa.freetuts.net về cách hiển thị chuyên mục đa cấp nên mình làm một bài kết hợp các kiểu đệ quy hiển thị chuyên mục đa cấp luôn.

test php

banquyen png
Bài viết này được đăng tại freetuts.net, không được copy dưới mọi hình thức.

Chúng ta có một số định dạng thường sử dụng khi đệ quy như sau:

  • Đệ quy hiển thị với table trong quản lý admin
  • Đệ quy hiển thị với thẻ select trong quản lý admin
  • Đệ quy với thẻ ul li trong trường hợp hiển thị menu

Tuy mình phân ra thành nhiều loại nhưng thực chất chúng ta chỉ dùng một thuật toán duy nhất đó là giải thuật đệ quy. Và giải thuật này chúng ta áp dụng cho cả menu đa cấp lẫn chuyên mục đa cấp.

Trước tiên chúng ta cần xây dựng một database để demo.

Bài viết này được đăng tại [free tuts .net]

1. Tạo database lưu trữ chuyên mục đa cấp

Để đơn giản thì mình sẽ xây dựng một database gồm ba fields chính đó là id, titleparent_id. Trong đó id là khóa chính và tăng tự động, title là tiêu đề của chuyên mục và parent_id là khóa ngoại trỏ đển chuyên mục cha. Trường hợp với cấp cao nhất thì sẽ có parent_id = 0.

Bạn tạo một CSDL demo và một bảng categories bằng cách chạy đoạn mã SQL sau.

SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";
SET time_zone = "+00:00";

CREATE DATABASE `demo` ;

USE DATABASE `demo`;


CREATE TABLE IF NOT EXISTS `categories` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `parent_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=11 ;

INSERT INTO `categories` (`id`, `title`, `parent_id`) VALUES
(1, 'PHP', 0),
(2, 'JAVASCRIPT', 0),
(3, 'Codeigniter', 1),
(4, 'Phalcon', 1),
(5, 'Tutorials', 3),
(6, 'AngularJS', 2),
(7, 'jQuery', 2),
(8, 'Basic', 5),
(9, 'Advance', 5),
(10, 'Course', 3);

Kết quả của database này chúng ta có một cây Categoy được phân cấp như sau:

de quy chuyen muc da cap png

2. Phân tích ý tưởng lấy dữ liệu để đệ quy

Hiện nay trên mạng có nhiều bài viết hướng dẫn đệ quy nhưng mình thấy đa số những bài viết đó sử dụng PHP thuần và mỗi lần đệ quy sẽ tốn một câu truy vấn nên dẫn đến chương trình chạy rất chậm.

Hầu hết các framework hiện nay đều hỗ trợ truy vấn database dạng Active Record và kết quả nó trả về một mảng được đánh số thứ tự từ 0 -> n-1, vì vậy trong bài này thay vì đệ quy và lặp kết quả thông qua vòng lặp while và hàm mysqli_fetch_assoc thì ta sẽ thực hiện qua 2 bước:

  • Bước 1: Lấy danh sách chuyên mục từ datavase và đưa vào một mảng, và đây chính là đoạn code php xử lý bước 1:
    // BƯỚC 1: LẤY DANH SÁCH CATEGORIES
    $conn = mysqli_connect('localhost', 'root', 'vertrigo', 'demo');
    
    $sql = 'SELECT * FROM categories';
    
    $result = mysqli_query($conn, $sql);
    
    $categories = array();
    
    while ($row = mysqli_fetch_assoc($result)){
        $categories[] = $row;
    }
  • Bước 2: Đệ quy mảng các chuyên mục có được ở bước 1. Tai bước này để tối ưu thì ta nên xóa phần tử đã được hiển thị bằng cách sử dụng hàm unset.
    // BƯỚC 2: HÀM ĐỆ QUY HIỂN THỊ CATEGORIES
    function showCategories($categories, $parent_id = 0, $char = '')
    {
        foreach ($categories as $key => $item)
        {
            // Nếu là chuyên mục con thì hiển thị
            if ($item['parent_id'] == $parent_id)
            {
                // Xử lý hiển thị chuyên mục
                // .....
                // .....
                // .....
                
                // Xóa chuyên mục đã lặp
                unset($categories[$key]);
                
                // Tiếp tục đệ quy để tìm chuyên mục con của chuyên mục đang lặp
                showCategories($categories, $item['id'], $char.'|---');
            }
        }
    }

Nhiệm vụ tiếp theo của bước 2 sẽ được trình bày ở các phần tiếp theo dưới đây.

3. Đệ quy chuyên mục đa cấp thẻ table trong admin

Chúng ta có 3 bước, trong đó có 2 bước mình đã bình bày ở trên rồi nên chỉ đăng code kèm comment thôi nhé.

// BƯỚC 1: LẤY DANH SÁCH CATEGORIES
$conn = mysqli_connect('localhost', 'root', 'vertrigo', 'demo');

$sql = 'SELECT * FROM categories';

$result = mysqli_query($conn, $sql);

$categories = array();

while ($row = mysqli_fetch_assoc($result)){
    $categories[] = $row;
}

// BƯỚC 2: HÀM ĐỆ QUY HIỂN THỊ CATEGORIES
function showCategories($categories, $parent_id = 0, $char = '')
{
    foreach ($categories as $key => $item)
    {
        // Nếu là chuyên mục con thì hiển thị
        if ($item['parent_id'] == $parent_id)
        {
            echo '<tr>';
                echo '<td>';
                    echo $char . $item['title'];
                echo '</td>';
            echo '</tr>';
            
            // Xóa chuyên mục đã lặp
            unset($categories[$key]);
            
            // Tiếp tục đệ quy để tìm chuyên mục con của chuyên mục đang lặp
            showCategories($categories, $item['id'], $char.'|---');
        }
    }
}

Bước tiếp theo là gọi hàm đệ quy hiển thị danh sách chuyên mục.

<table border="1" cellspacing="0" cellpadding="5">
    <tr>
        <td><strong>Chuyên mục</strong></td>
    </tr>
    <?php showCategories($categories); ?>
</table>

Và hình ảnh sau là thành quả của chúng ta:

de quy chuyen muc da cap php 1 png

3. Đệ quy chuyên mục đa cấp thẻ select option

Đối với dạng này ta chỉ cần chỉnh một chút trong hàm hiển thị chuyên mục là được.

// BƯỚC 2: HÀM ĐỆ QUY HIỂN THỊ CATEGORIES
function showCategories($categories, $parent_id = 0, $char = '')
{
    foreach ($categories as $key => $item)
    {
        // Nếu là chuyên mục con thì hiển thị
        if ($item['parent_id'] == $parent_id)
        {
            echo '<option value="'.$item[$key].'">';
                echo $char . $item['title'];
            echo '</option>';
            
            // Xóa chuyên mục đã lặp
            unset($categories[$key]);
            
            // Tiếp tục đệ quy để tìm chuyên mục con của chuyên mục đang lặp
            showCategories($categories, $item['id'], $char.'|---');
        }
    }
}

Và đây là code hiển thị thẻ select chuyên mục:

<select>
    <?php showCategories($categories); ?>
</select>

Và đây là thành quả:

de quy chuyen muc da cap php 3 png

4. Đệ quy chuyên mục đa cấp với thẻ UL và LI

Với chức năng này hơi khó một chút xíu vì bạn sẽ phải xử lý để lúc hiển thị không thị dư thẻ UL. Để thực hiện được điều này ta phải chia bước 2 thành hai bước nhỏ như sau:

  • Bước 2.1: Lấy danh sách chuyên mục con
  • Bước 2.2: Kiểm tra nếu có chuyên mục con thì hiển thị thẻ UL, đồng thời lặp và hiển thị thẻ LI.

// BƯỚC 2: HÀM ĐỆ QUY HIỂN THỊ CATEGORIES
function showCategories($categories, $parent_id = 0, $char = '')
{
    // BƯỚC 2.1: LẤY DANH SÁCH CATE CON
    $cate_child = array();
    foreach ($categories as $key => $item)
    {
        // Nếu là chuyên mục con thì hiển thị
        if ($item['parent_id'] == $parent_id)
        {
            $cate_child[] = $item;
            unset($categories[$key]);
        }
    }
    
    // BƯỚC 2.2: HIỂN THỊ DANH SÁCH CHUYÊN MỤC CON NẾU CÓ
    if ($cate_child)
    {
        echo '<ul>';
        foreach ($cate_child as $key => $item)
        {
            // Hiển thị tiêu đề chuyên mục
            echo '<li>'.$item['title'];
            
            // Tiếp tục đệ quy để tìm chuyên mục con của chuyên mục đang lặp
            showCategories($categories, $item['id'], $char.'|---');
            echo '</li>';
        }
        echo '</ul>';
    }
}

Và đây là cách sử dụng:

<?php showCategories($categories); ?>

Và đây là thành quả:

de quy chuyen muc da cap php 2 png

Còn một vấn đề nữa đó là xử lý hiển thị class cho thẻ UL - LI cha và các thẻ UL - LI con, để xử lý trường hợp này thì ta thêm một tham số $stt có giá trị ban đầu = 0, và sau mỗi lần đệ quy sẽ tăng $stt lên 1 đơn vị. Lúc này để kiểm tra cấp để bổ sung class thì chỉ cần kiểm tra biến $stt.

// BƯỚC 2: HÀM ĐỆ QUY HIỂN THỊ CATEGORIES
function showCategories($categories, $parent_id = 0, $char = '', $stt = 0)
{
    // BƯỚC 2.1: LẤY DANH SÁCH CATE CON
    $cate_child = array();
    foreach ($categories as $key => $item)
    {
        // Nếu là chuyên mục con thì hiển thị
        if ($item['parent_id'] == $parent_id)
        {
            $cate_child[] = $item;
            unset($categories[$key]);
        }
    }
    
    // BƯỚC 2.2: HIỂN THỊ DANH SÁCH CHUYÊN MỤC CON NẾU CÓ
    if ($cate_child)
    {
        if ($stt == 0){
            // là cấp 1
        }
        else if ($stt == 1){
            // là cấp 2
        }
        else if ($stt == 2){
            // là cấp 3
        }
        
        echo '<ul>';
        foreach ($cate_child as $key => $item)
        {
            // Hiển thị tiêu đề chuyên mục
            echo '<li>'.$item['title'];
            // Tiếp tục đệ quy để tìm chuyên mục con của chuyên mục đang lặp
            showCategories($categories, $item['id'], $char.'|---', ++$stt);
            echo '</li>';
        }
        echo '</ul>';
    }
}

Quá đơn giản phải không các bạn.

5. Lời kết

Những cách đệ quy trên mình chỉ xử lý ở dạng thô sơ nên để đáp ứng với giao diện của bạn thì hãy bổ sung các class, id cho phù hợp nhé.

Đây là cách mình chia sẻ nên nếu bạn có cách nào hay hơn thì hãy chia sẻ thêm để mọi người cùng học hỏi nhé.

Chúc bạn thành công!

Cùng chuyên mục:

Hàm key_exists() trong PHP

Hàm key_exists() trong PHP

Cách sử dụng key_exists() trong PHP

Hàm mysqli_fetch_row() trong PHP

Hàm mysqli_fetch_row() trong PHP

Cách sử dụng mysqli_fetch_row() trong PHP

Hàm end() trong PHP

Hàm end() trong PHP

Cách sử dụng end() trong PHP

Hàm mysqli_field_count() trong PHP

Hàm mysqli_field_count() trong PHP

Cách sử dụng mysqli_field_count() trong PHP

Hàm count() trong PHP

Hàm count() trong PHP

Cách sử dụng count() trong PHP

Hàm mysqli_field_seek() trong PHP

Hàm mysqli_field_seek() trong PHP

Cách sử dụng mysqli_field_seek() trong PHP

Hàm compact() trong PHP

Hàm compact() trong PHP

Cách sử dụng compact() trong PHP

Hàm mysqli_field_tell() trong PHP

Hàm mysqli_field_tell() trong PHP

Cách sử dụng mysqli_field_tell() trong PHP

Hàm array_values() trong PHP

Hàm array_values() trong PHP

Cách sử dụng array_values() trong PHP

Hàm mysqli_free_result() trong PHP

Hàm mysqli_free_result() trong PHP

Cách sử dụng mysqli_free_result() trong PHP

Hàm array_unshift() trong PHP

Hàm array_unshift() trong PHP

Cách sử dụng array_unshift() trong PHP

Hàm mysqli_get_charset() trong PHP

Hàm mysqli_get_charset() trong PHP

Cách sử dụng mysqli_get_charset() trong PHP

Hàm array_shift() trong PHP

Hàm array_shift() trong PHP

Cách sử dụng array_shift() trong PHP

Hàm mysqli_get_client_stats() trong PHP

Hàm mysqli_get_client_stats() trong PHP

Cách sử dụng mysqli_get_client_stats() trong PHP

Hàm array_unique() trong PHP

Hàm array_unique() trong PHP

Cách sử dụng array_unique() trong PHP

Hàm mysqli_get_client_version() trong PHP

Hàm mysqli_get_client_version() trong PHP

Cách sử dụng mysqli_get_client_version() trong PHP

Hàm array_uintesect() trong PHP

Hàm array_uintesect() trong PHP

Cách sử dụng array_uintesect() trong PHP

Hàm mysqli_get_connection_stats() trong PHP

Hàm mysqli_get_connection_stats() trong PHP

Cách sử dụng mysqli_get_connection_stats() trong PHP

Hàm array_sum() trong PHP

Hàm array_sum() trong PHP

Cách sử dụng array_sum() trong PHP

Hàm mysqli_get_host_info() trong PHP

Hàm mysqli_get_host_info() trong PHP

Cách sử dụng mysqli_get_host_info() trong PHP

Top