2011-12-21 35 views
63

Tôi đang tìm cách viết một hàm trang/danh mục (từ kết quả cơ sở dữ liệu phẳng) và tạo một mảng các mục trang/danh mục lồng nhau dựa trên id mẹ . Tôi muốn làm điều này một cách đệ quy, để bất kỳ mức độ làm tổ nào cũng có thể được thực hiện.Chức năng đệ quy để tạo mảng đa chiều từ kết quả cơ sở dữ liệu

Ví dụ: Tôi lấy tất cả các trang trong một truy vấn, và đây là những gì các bảng cơ sở dữ liệu trông giống như

+-------+---------------+---------------------------+ 
| id | parent_id |   title   | 
+-------+---------------+---------------------------+ 
| 1 |  0  | Parent Page    | 
| 2 |  1  | Sub Page    | 
| 3 |  2  | Sub Sub Page   | 
| 4 |  0  | Another Parent Page  | 
+-------+---------------+---------------------------+ 

Và đây là mảng tôi muốn kết thúc với xử lý trong xem tác phẩm của tôi:

Array 
(
    [0] => Array 
     (
      [id] => 1 
      [parent_id] => 0 
      [title] => Parent Page 
      [children] => Array 
         (
          [0] => Array 
           (
            [id] => 2 
            [parent_id] => 1 
            [title] => Sub Page 
            [children] => Array 
               (
                [0] => Array 
                 (
                  [id] => 3 
                  [parent_id] => 1 
                  [title] => Sub Sub Page 
                 ) 
               ) 
           ) 
         ) 
     ) 
    [1] => Array 
     (
      [id] => 4 
      [parent_id] => 0 
      [title] => Another Parent Page 
     ) 
) 

tôi đã nhìn và cố gắng gần như mọi giải pháp tôi đã đi qua (có rất nhiều trong số họ ở đây trên stack Overflow, nhưng đã không có may mắn nhận được một cái gì đó đủ chung chung mà sẽ làm việc cho cả hai các trang và loại.

Đây là gần nhất tôi đã nhận được, nhưng nó không hoạt động vì tôi chỉ định trẻ em cho cấp độ cha mẹ đầu tiên.

function page_walk($array, $parent_id = FALSE) 
{ 
    $organized_pages = array(); 

    $children = array(); 

    foreach($array as $index => $page) 
    { 
     if ($page['parent_id'] == 0) // No, just spit it out and you're done 
     { 
      $organized_pages[$index] = $page; 
     } 
     else // If it does, 
     {  
      $organized_pages[$parent_id]['children'][$page['id']] = $this->page_walk($page, $parent_id); 
     } 
    } 

    return $organized_pages; 
} 

function page_list($array) 
{  
    $fakepages = array(); 
    $fakepages[0] = array('id' => 1, 'parent_id' => 0, 'title' => 'Parent Page'); 
    $fakepages[1] = array('id' => 2, 'parent_id' => 1, 'title' => 'Sub Page'); 
    $fakepages[2] = array('id' => 3, 'parent_id' => 2, 'title' => 'Sub Sub Page'); 
    $fakepages[3] = array('id' => 4, 'parent_id' => 3, 'title' => 'Another Parent Page'); 

    $pages = $this->page_walk($fakepages, 0); 

    print_r($pages); 
} 
+1

Có thể bạn không chỉ làm việc với một mảng của tất cả parent_ids và một mảng cho các trang của bạn? – djot

Trả lời

169

Một số rất đơn giản, chung chung xây dựng cây:

function buildTree(array $elements, $parentId = 0) { 
    $branch = array(); 

    foreach ($elements as $element) { 
     if ($element['parent_id'] == $parentId) { 
      $children = buildTree($elements, $element['id']); 
      if ($children) { 
       $element['children'] = $children; 
      } 
      $branch[] = $element; 
     } 
    } 

    return $branch; 
} 

$tree = buildTree($rows); 

Thuật toán là khá đơn giản:

  1. Hãy mảng của tất cả các yếu tố và id của phụ huynh hiện hành (ban đầu 0/không có gì/null/bất cứ điều gì).
  2. Lặp qua tất cả các yếu tố.
  3. Nếu parent_id của phần tử khớp với id chính hiện tại bạn có trong 1., phần tử là phần tử con của cha/mẹ. Đặt nó vào danh sách trẻ em hiện tại của bạn (ở đây: $branch).
  4. Gọi hàm theo cách đệ quy với id của phần tử bạn vừa xác định trong 3., nghĩa là tìm tất cả trẻ em của phần tử đó và thêm chúng làm yếu tố children.
  5. Trả lại danh sách trẻ em đã tìm thấy.

Nói cách khác, một thực thi hàm này trả về danh sách các phần tử là con của id mẹ đã cho. Gọi nó với buildTree($myArray, 1), nó sẽ trả về một danh sách các phần tử có id cha 1. Ban đầu hàm này được gọi với id mẹ là 0, vì vậy các phần tử không có id cha được trả về, là các nút gốc. Hàm này gọi chính nó một cách đệ quy để tìm con cái của trẻ.

+0

Điều này hoạt động hoàn hảo. Tôi có thể nhìn thấy nơi tôi đã đi sai trong logic của phiên bản của tôi. Cách để đá nó! Tôi không cần phải sử dụng đệ quy rất thường xuyên trong các dự án, vì vậy điều này chắc chắn sẽ giúp một tấn. –

+1

Rất vui được. Lưu ý: điều này có phần không hiệu quả vì nó luôn truyền toàn bộ mảng '$ elements'. Đối với các mảng nhỏ mà hầu như không quan trọng, nhưng đối với các tập dữ liệu lớn, bạn sẽ muốn loại bỏ phần tử đã so khớp khỏi nó trước khi chuyển nó xuống. Điều đó trở nên hơi lộn xộn mặc dù, vì vậy tôi để nó đơn giản cho sự hiểu biết của bạn dễ dàng hơn. :) – deceze

+5

@deceze Tôi cũng muốn xem phiên bản lộn xộn. Cảm ơn trước! –

0

Có thể sử dụng php để lấy kết quả mysql thành mảng và sau đó sử dụng nó.

$categoryArr = Array(); 
while($categoryRow = mysql_fetch_array($category_query_result)){ 
    $categoryArr[] = array('parentid'=>$categoryRow['parent_id'], 
      'id'=>$categoryRow['id']); 
    } 
9

Tôi biết câu hỏi này cũ, nhưng tôi đã đối mặt với một vấn đề rất giống nhau - ngoại trừ với lượng dữ liệu rất lớn. Sau khi một số cuộc đấu tranh, tôi quản lý để xây dựng cây trong một pass của resultset - sử dụng tài liệu tham khảo. Mã này không đẹp, nhưng nó hoạt động và nó hoạt động khá nhanh.Đó là không đệ quy - có nghĩa là, chỉ có một đường chuyền qua resultset và sau đó một array_filter ở cuối:

$dbh = new PDO(CONNECT_STRING, USERNAME, PASSWORD); 
$dbs = $dbh->query("SELECT n_id, n_parent_id from test_table order by n_parent_id, n_id"); 
$elems = array(); 

while(($row = $dbs->fetch(PDO::FETCH_ASSOC)) !== FALSE) { 
    $row['children'] = array(); 
    $vn = "row" . $row['n_id']; 
    ${$vn} = $row; 
    if(!is_null($row['n_parent_id'])) { 
     $vp = "parent" . $row['n_parent_id']; 
     if(isset($data[$row['n_parent_id']])) { 
      ${$vp} = $data[$row['n_parent_id']]; 
     } 
     else { 
      ${$vp} = array('n_id' => $row['n_parent_id'], 'n_parent_id' => null, 'children' => array()); 
      $data[$row['n_parent_id']] = &${$vp}; 
     } 
     ${$vp}['children'][] = &${$vn}; 
     $data[$row['n_parent_id']] = ${$vp}; 
    } 
    $data[$row['n_id']] = &${$vn}; 
} 
$dbs->closeCursor(); 

$result = array_filter($data, function($elem) { return is_null($elem['n_parent_id']); }); 
print_r($result); 

Khi thực hiện trên dữ liệu này:

mysql> select * from test_table; 
+------+-------------+ 
| n_id | n_parent_id | 
+------+-------------+ 
| 1 |  NULL | 
| 2 |  NULL | 
| 3 |   1 | 
| 4 |   1 | 
| 5 |   2 | 
| 6 |   2 | 
| 7 |   5 | 
| 8 |   5 | 
+------+-------------+ 

cuối cùng print_r sản xuất sản lượng này:

Array 
(
    [1] => Array 
     (
      [n_id] => 1 
      [n_parent_id] => 
      [children] => Array 
       (
        [3] => Array 
         (
          [n_id] => 3 
          [n_parent_id] => 1 
          [children] => Array 
           (
           ) 

         ) 

        [4] => Array 
         (
          [n_id] => 4 
          [n_parent_id] => 1 
          [children] => Array 
           (
           ) 

         ) 

       ) 

     ) 

    [2] => Array 
     (
      [n_id] => 2 
      [n_parent_id] => 
      [children] => Array 
       (
        [5] => Array 
         (
          [n_id] => 5 
          [n_parent_id] => 2 
          [children] => Array 
           (
            [7] => Array 
             (
              [n_id] => 7 
              [n_parent_id] => 5 
              [children] => Array 
               (
               ) 

             ) 

            [8] => Array 
             (
              [n_id] => 8 
              [n_parent_id] => 5 
              [children] => Array 
               (
               ) 

             ) 

           ) 

         ) 

        [6] => Array 
         (
          [n_id] => 6 
          [n_parent_id] => 2 
          [children] => Array 
           (
           ) 

         ) 

       ) 

     ) 

) 

Đó chính xác là những gì tôi đang tìm kiếm.

+0

Bạn thật tuyệt vời. Điều này làm việc hoàn hảo cho tôi. Tôi đã tìm kiếm điều này kể từ 2 ngày qua. Câu trả lời này nên được đánh dấu là câu trả lời tốt nhất. – Swadesh

+0

trong khi giải pháp là thông minh, nhưng mã này có lỗi, nó đã cho tôi kết quả khác nhau về các tình huống khác nhau – Mohammadhzp

+0

@Mohammadhzp Tôi đã sử dụng giải pháp này trong sản xuất cho năm ngoái và không có vấn đề với nó.Nếu dữ liệu của bạn khác nhau, bạn sẽ nhận được kết quả khác nhau :) –

0

Đối với mảng lớn:

truyền mảng theo tham chiếu.

function buildTree(&$elements, $parentId = 0) { //pass main array by reference 
    $branch = array(); 

    foreach ($elements as $key => $element) { 
     if ($element['parent_id'] == $parentId) { 

      $element['children'] = buildTree($elements, $element['id']); 

      $branch[] = $element;     
     } 
    } 
    return $branch; 
} 

$tree = buildTree($rows); 

Nếu bạn vượt qua mảng có tài liệu tham khảo sau đó cùng một mảng sẽ sử dụng trong tất cả các chức năng đệ quy không cần phải sáp nhập mảng con mẹ ở cuối

Các vấn đề liên quan