2017-06-27 24 views
6

Tôi đang tạo một mảng menu lồng nhau từ phản hồi mà tôi nhận được từ cơ sở dữ liệu WP. Tôi nhận được dữ liệu từ WP trong bộ điều khiển trong Laravel với sự giúp đỡ của corcel package, và sau đó tạo một mảng với dữ liệu trình đơn, mà bây giờ là một cấp độ sâu. Vì vậy, khi một liên kết đơn có một liên kết menu con, mảng trông như thế này:PHP - Tạo cấu trúc menu cây lồng nhau từ một mảng phẳng

{ 
    "Hjem": { 
     "ID": 112, 
     "title": "Hjem", 
     "slug": "hjem", 
     "url": "http://hivnorge.app/?p=112", 
     "status": "publish", 
     "main_category": "Hovedmeny", 
     "submenus": [ 
      { 
       "ID": 129, 
       "title": "Lorem ipsum", 
       "slug": "lorem-ipsum", 
       "url": "http://hivnorge.app/?p=129", 
       "status": "publish", 
       "main_category": "Nyheter" 
      } 
     ] 
    }, 
    "Nytt test innlegg": { 
     "ID": 127, 
     "title": "Nytt test innlegg", 
     "slug": "nytt-test-innlegg", 
     "url": "http://hivnorge.app/?p=127", 
     "status": "private", 
     "main_category": "Nyheter", 
     "submenus": [ 
      { 
       "ID": 125, 
       "title": "Test innlegg", 
       "slug": "test-innlegg", 
       "url": "http://hivnorge.app/?p=125", 
       "status": "publish", 
       "main_category": "Nyheter" 
      }, 
      { 
       "ID": 129, 
       "title": "Lorem ipsum", 
       "slug": "lorem-ipsum", 
       "url": "http://hivnorge.app/?p=129", 
       "status": "publish", 
       "main_category": "Nyheter" 
      } 
     ] 
    }, 
    "Prosjektsamarbeidets verdi": { 
     "ID": 106, 
     "title": "Prosjektsamarbeidets verdi", 
     "slug": "prosjektsamarbeidets-verdi", 
     "url": "http://hivnorge.no.wordpress.seven.fredrikst/?p=106", 
     "status": "publish", 
     "main_category": "Prevensjon" 
    } 
} 

Đây là cách tôi đang tạo ra phản ứng này:

 $menu = Menu::slug('hovedmeny')->first(); 
     $res = []; 

     foreach ($menu->nav_items as $item) { 
      $item->makeHidden($hiddenAttributes)->toArray(); 
      $parent_id = $item->meta->_menu_item_menu_item_parent; 

      if ($parent_id == '0') { 
       if ($item->title == '') { 
        $item = $this->findPost($item); 
       } 
       $parentItem = $item; 
       $res[$parentItem->title] = $parentItem->makeHidden($hiddenAttributes)->toArray(); 
      } 
      else { 
       $childItem = $this->findPost($item); 
       $res[$parentItem->title]['submenus'][] = $childItem->makeHidden($hiddenAttributes)->toArray(); 
      } 
     } 

     return $res; 

Vấn đề là tôi có câu trả lời từ WP chỉ trả parent_id cho mỗi $item và không có dữ liệu về nếu một mục có một số trẻ em, vì vậy đây là các dữ liệu meta của mặt hàng đó mẹ ví dụ:

  #attributes: array:4 [ 
      "meta_id" => 209 
      "post_id" => 112 
      "meta_key" => "_menu_item_menu_item_parent" 
      "meta_value" => "0" 
      ] 

và đây là lần thứ e dữ liệu meta của mục con:

  #attributes: array:4 [ 
      "meta_id" => 326 
      "post_id" => 135 
      "meta_key" => "_menu_item_menu_item_parent" 
      "meta_value" => "112" 
      ] 

Làm cách nào để tôi có thể thực hiện điều này linh hoạt và cho phép lồng sâu hơn để tôi có thể có menu con bên trong menu phụ?

Tôi đã cố gắng tìm giải pháp here, bởi vì đó là khá nhiều vấn đề tương tự như của tôi, nhưng không thể triển khai nó. Trong các mục menu mảng của tôi cũng chỉ có parent_idparent_id0 được coi là phần tử gốc. Ngoài ra, parent_id nếu số menu itempost, trỏ đến meta id và không phải là id của post mà tôi cần, vì vậy tôi cần phải nhận số bổ sung đó từ meta->_menu_item_object_id.

CẬP NHẬT

tôi đã được quản lý để thực hiện một cây như cấu trúc, nhưng vấn đề tôi có bây giờ là tôi không biết làm thế nào để có được những title cho các yếu tố menu là posts. Tôi đã làm điều đó trong ví dụ trước bằng cách kiểm tra nếu title trống sau đó tôi sẽ tìm kiếm mà post bởi id:

  if ($item->title == '') { 
       $item = $this->findPost($item); 
      } 

Nhưng, với mã mới, nơi tôi đang làm cho một cây giống như cấu trúc Tôi không chắc chắn làm thế nào để làm điều đó, kể từ đó tôi không thể làm cho cấu trúc cây, kể từ khi tôi so sánh tất cả mọi thứ với các id, và ids của phần tử menu khác với id của post được trỏ đến, vì vậy sau đó tôi không thể tạo cấu trúc cây:

private function menuBuilder($menuItems, $parentId = 0) 
    { 
     $hiddenAttributes = \Config::get('middleton.wp.menuHiddenAttributes'); 
     $res = []; 

     foreach ($menuItems as $index => $item) { 
      $itemParentId = $item->meta->_menu_item_menu_item_parent; 

      if ($itemParentId == $parentId) { 
       $children = self::menuBuilder($menuItems, $item->ID); 

       if ($children) { 
        $item['submenu'] = $children; 
       } 

       $res[$item->ID] = $item->makeHidden($hiddenAttributes)->toArray(); 
       unset($menuItems[$index]); 
      } 
     } 

     return $res; 
    } 

Vì vậy, sau đó dữ liệu tôi nhận được là:

{ 
    "112": { 
     "ID": 112, 
     "submenu": { 
      "135": { 
       "ID": 135, 
       "title": "", 
       "slug": "135", 
       "url": "http://hivnorge.app/?p=135", 
       "status": "publish", 
       "main_category": "Hovedmeny" 
      } 
     }, 
     "title": "Hjem", 
     "slug": "hjem", 
     "url": "http://hivnorge.app/?p=112", 
     "status": "publish", 
     "main_category": "Hovedmeny" 
    }, 
    "136": { 
     "ID": 136, 
     "submenu": { 
      "137": { 
       "ID": 137, 
       "submenu": { 
        "138": { 
         "ID": 138, 
         "title": "", 
         "slug": "138", 
         "url": "http://hivnorge.app/?p=138", 
         "status": "publish", 
         "main_category": "Hovedmeny" 
        } 
       }, 
       "title": "", 
       "slug": "137", 
       "url": "http://hivnorge.app/?p=137", 
       "status": "publish", 
       "main_category": "Hovedmeny" 
      } 
     }, 
     "title": "", 
     "slug": "136", 
     "url": "http://hivnorge.app/?p=136", 
     "status": "publish", 
     "main_category": "Hovedmeny" 
    }, 
    "139": { 
     "ID": 139, 
     "title": "", 
     "slug": "139", 
     "url": "http://hivnorge.app/?p=139", 
     "status": "publish", 
     "main_category": "Hovedmeny" 
    } 
} 

Trả lời

1

Một cách để giải quyết này để làm sử dụng bí danh biến.Nếu bạn quan tâm đến việc quản lý một bảng tra cứu (mảng) cho các ID, bạn có thể sử dụng nó để chèn vào đúng vị trí của mảng menu phân cấp dưới dạng các biến khác nhau (tại đây các mục mảng trong bảng tra cứu) có thể tham chiếu cùng một giá trị .

Trong ví dụ sau, điều này được minh chứng. Nó cũng giải quyết vấn đề thứ hai (ẩn trong câu hỏi của bạn) rằng mảng phẳng không được sắp xếp (thứ tự là không xác định trong bảng kết quả cơ sở dữ liệu), do đó một mục menu phụ có thể nằm trong kết quả trước mục nhập menu mục con đến.

Ví dụ tôi tạo một mảng phẳng đơn giản:

# some example rows as the flat array 
$rows = [ 
    ['id' => 3, 'parent_id' => 2, 'name' => 'Subcategory A'], 
    ['id' => 1, 'parent_id' => null, 'name' => 'Home'], 
    ['id' => 2, 'parent_id' => null, 'name' => 'Categories'], 
    ['id' => 4, 'parent_id' => 2, 'name' => 'Subcategory B'], 
]; 

Sau đó cho công việc để làm thì có hai biến chính: Thứ nhất các $menu đó là mảng thứ bậc để tạo ra và thứ hai $byId đó là tra cứu bảng:

# initialize the menu structure 
$menu = []; # the menu structure 
$byId = []; # menu ID-table (temporary) 

Bảng tra cứu chỉ cần thiết miễn là menu được tạo, nó sẽ bị vứt đi sau đó.

Bước lớn tiếp theo là tạo $menu bằng cách di chuyển qua mảng phẳng. Đây là một vòng lặp foreach lớn:

# build the menu (hierarchy) from flat $rows traversable 
foreach ($rows as $row) { 
    # map row to local ID variables 
    $id = $row['id']; 
    $parentId = $row['parent_id']; 

    # build the entry 
    $entry = $row; 
    # init submenus for the entry 
    $entry['submenus'] = &$byId[$id]['submenus']; # [1] 

    # register the entry in the menu structure 
    if (null === $parentId) { 
     # special case that an entry has no parent 
     $menu[] = &$entry; 
    } else { 
     # second special case that an entry has a parent 
     $byId[$parentId]['submenus'][] = &$entry; 
    } 

    # register the entry as well in the menu ID-table 
    $byId[$id] = &$entry; 

    # unset foreach (loop) entry alias 
    unset($entry); 
} 

Đây là nơi các mục được ánh xạ từ các mảng phẳng ($rows) vào $menu mảng thứ bậc. Không cần đệ quy nào nhờ vào ngăn xếp và bảng tra cứu $byId.

Điểm mấu chốt ở đây là sử dụng bí danh biến (tham chiếu) khi thêm mục nhập mới vào cấu trúc $menu cũng như khi thêm chúng vào $byId. Điều này cho phép truy cập vào cùng một giá trị trong bộ nhớ với hai tên biến khác nhau:

 # special case that an entry has no parent 
     $menu[] = &$entry; 
     ... 

    # register the entry as well in the menu ID-table 
    $byId[$id] = &$entry; 

Nó được thực hiện với sự phân công = & và nó có nghĩa là $byId[$id] cho phép truy cập đến $menu[<< new key >>].

Cùng được thực hiện trong trường hợp nó được thêm vào một menu con:

# second special case that an entry has a parent 
    $byId[$parentId]['submenus'][] = &$entry; 
... 

# register the entry as well in the menu ID-table 
$byId[$id] = &$entry; 

Đây $byId[$id] điểm để $menu...[ << parent id entry in the array >>]['submenus'][ << new key >> ].

Điều này giải quyết vấn đề luôn tìm đúng nơi để chèn mục nhập mới vào cấu trúc phân cấp.

Để xử lý các trường hợp menu con xuất hiện trong mảng phẳng trước khi nhập mục menu, menu phụ khi được khởi tạo cho mục nhập mới cần được đưa ra khỏi bảng tra cứu (tại [1]):

# init submenus for the entry 
$entry['submenus'] = &$byId[$id]['submenus']; # [1] 

Đây là một trường hợp đặc biệt. Trong trường hợp $byId[$id]['submenus'] chưa được đặt (ví dụ: trong vòng đầu tiên), nó được đặt ngầm thành null do tham chiếu (& ở phía trước &$byId[$id]['submenus']). Trong trường hợp nó được thiết lập, menu con hiện có từ một mục nhập chưa tồn tại sẽ được sử dụng để khởi tạo menu phụ của mục nhập.

Làm như vậy là đủ để không phụ thuộc vào bất kỳ thứ tự cụ thể nào trong $rows.

Đây là những gì vòng lặp thực hiện.

Phần còn lại là công việc dọn dẹp:

# unset ID aliases 
unset($byId); 

Nó unsets bảng look ID như nó không cần thiết nữa. Nghĩa là, tất cả các bí danh đều không được đặt.

Để hoàn thành ví dụ:

# visualize the menu structure 
print_r($menu); 

Mà sau đó cung cấp cho các đầu ra sau đây:

Array 
(
    [0] => Array 
     (
      [id] => 1 
      [parent_id] => 
      [name] => Home 
      [submenus] => 
     ) 

    [1] => Array 
     (
      [id] => 2 
      [parent_id] => 
      [name] => Categories 
      [submenus] => Array 
       (
        [0] => Array 
         (
          [id] => 3 
          [parent_id] => 2 
          [name] => Subcategory A 
          [submenus] => 
         ) 

        [1] => Array 
         (
          [id] => 4 
          [parent_id] => 2 
          [name] => Subcategory B 
          [submenus] => 
         ) 

       ) 

     ) 

) 

Tôi hy vọng điều này là dễ hiểu và bạn có thể áp dụng điều này vào kịch bản cụ thể của bạn. Bạn có thể bọc nó trong một chức năng riêng của nó (mà tôi đề nghị), tôi chỉ giữ nó tiết lộ ví dụ để minh họa tốt hơn các phần.

liên quan Q & Một vật liệu:

+0

Nếu bạn muốn đọc thêm về các tài liệu tham khảo trong PHP, hướng dẫn có một phần cho nó http://php.net/manual/en/language.references.php – hakre

+0

Cảm ơn bạn rất nhiều cho câu trả lời chi tiết của bạn! – Leff

+0

Cảm ơn bạn vì tiền thưởng tốt đẹp đó! Nếu nó hữu ích, xin vui lòng xem xét một upvote của câu trả lời. – hakre

1

Vì vậy, bạn sẽ cần phải viết một hàm đệ quy thấy What is a RECURSIVE Function in PHP?

Vì vậy, một cái gì đó giống như

function menuBuilder($menuItems){ 
    foreach($menuItems as $key => $item) 
    { 
     if(!empty($item->children)){ 
      $output[$key] = menuBuilder($item->children); 
     } 
    } 
    return $output; 
} 
+2

Tôi không chắc chắn làm thế nào để làm điều đó, vì tôi không thể kiểm tra hoặc nhìn thấy từ phản ứng Tôi nhận được từ WP, nếu menuItem có con, tôi chỉ có thể nhìn thấy nếu mục có một phụ huynh, và sau đó tôi không chắc chắn làm thế nào để đặt nó vào vị trí chính xác trong mảng. – Leff

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