Ubah output item nav_menu menjadi array multidimensi seperti pohon

11

Apakah ada cara untuk mengambil item menu nav sebagai array multidimensi daripada array datar?

Dengan struktur seperti pohon, saya bermaksud sesuatu yang akan menjaga hubungan antara barang-barang anak dan orang tua, seperti itu (ini hanya sebuah contoh) ...

array(
  array(
    'post_type' => 'page',
    'post_name' => 'Home',
    'children' => array() 
  ),
  array(
    'post_type' => 'page',
    'post_name' => 'About Us',
    'children' => array(
      array(
        'post_type' => 'page',
        'post_name' => 'Our History',
        'children' => array() 
      )
    ) 
  )
)

Ada wp_get_nav_menu_items()fungsi tetapi mengembalikan array 1 dimensi dengan semua item pada level yang sama, yang bukan yang saya inginkan. Apakah WordPress menyertakan cara bawaan untuk mendapatkan array multidimensi untuk item menu saya? Jika tidak, apa cara terbaik untuk mendapatkan wp_get_nav_menu_items()struktur seperti pohon ke dalam array multidimensi dalam hal kinerja?

YemSalat
sumber
3
array 1 dimensi itu berisi semua data yang Anda butuhkan untuk membangun pohon jika Anda menggunakan fungsi rekursif. untuk setiap ID item menu, cari item menu lainnya dengan ID yang cocok di bidang induk objek, mereka akan menjadi anak-anaknya.
Milo
Saya tahu saya bisa membuat pohon dari itu, tapi saya bertanya-tanya apakah sudah ada opsi seperti itu di wp.
YemSalat
Apa kasus penggunaan Anda? The Walkerkelas menangani kedalaman item menu nav diurutkan secara otomatis, bahkan jika array datar.
Matt van Andel
1
Hasil edit Anda salah. Saya mengedit judul kembali (mengubah beberapa kata) Output dari nav_items adalah array datar, ini bukan pohon dalam arti apa pun. Kasus penggunaan saya adalah - Saya ingin item nav sebagai pohon, jadi saya bisa melakukan sendiri dengan itu, tanpa harus menggunakan abstraksi WP yang rusak.
YemSalat
Saya sedikit mengklarifikasi pertanyaan, untuk membuatnya lebih jelas apa yang saya inginkan.
YemSalat

Jawaban:

21

Masalah membangun pohon dari susunan datar telah diselesaikan di sini dengan solusi rekursif yang sedikit dimodifikasi:

/**
 * Modification of "Build a tree from a flat array in PHP"
 *
 * Authors: @DSkinner, @ImmortalFirefly and @SteveEdson
 *
 * @link https://stackoverflow.com/a/28429487/2078474
 */
function buildTree( array &$elements, $parentId = 0 )
{
    $branch = array();
    foreach ( $elements as &$element )
    {
        if ( $element->menu_item_parent == $parentId )
        {
            $children = buildTree( $elements, $element->ID );
            if ( $children )
                $element->wpse_children = $children;

            $branch[$element->ID] = $element;
            unset( $element );
        }
    }
    return $branch;
}

tempat kami menambahkan wpse_childrenatribut awalan untuk menghindari tabrakan nama.

Sekarang kita hanya perlu mendefinisikan fungsi pembantu sederhana:

/**
 * Transform a navigational menu to it's tree structure
 *
 * @uses  buildTree()
 * @uses  wp_get_nav_menu_items()
 *
 * @param  String     $menud_id 
 * @return Array|null $tree 
 */
function wpse_nav_menu_2_tree( $menu_id )
{
    $items = wp_get_nav_menu_items( $menu_id );
    return  $items ? buildTree( $items, 0 ) : null;
}

Sekarang menjadi sangat mudah untuk mengubah menu navigasi menjadi struktur pohon dengan:

$tree = wpse_nav_menu_2_tree( 'my_menu' );  // <-- Modify this to your needs!
print_r( $tree );

Untuk JSON, kita cukup menggunakan:

$json = json_encode( $tree );

Untuk versi yang sedikit berbeda, di mana kami memilih sendiri atributnya, lihat revisi pertama dari jawaban ini di sini .

Pembaruan: Kelas Walker

Inilah ide yang agak samar bagaimana kita dapat mencoba menghubungkan ke bagian rekursif dari display_element()metode Walkerkelas abstrak .

$w = new WPSE_Nav_Menu_Tree;
$args = (object) [ 'items_wrap' => '', 'depth' => 0, 'walker' => $w ];
$items = wp_get_nav_menu_items( 'my_menu' );
walk_nav_menu_tree( $items, $args->depth, $args );
print_r( $w->branch );  

di mana WPSE_Nav_Menu_Treemerupakan perluasan dari Walker_Nav_Menukelas:

class WPSE_Nav_Menu_Tree extends Walker_Nav_Menu
{
   public $branch = [];

   public function display_element($element, &$children, $max_depth, $depth = 0, $args, &$output )
   {
      if( 0 == $depth )
         $this->branch[$element->ID] = $element;

      if ( isset($children[$element->ID] ) )
         $element->wpse_children = $children[$element->ID];

      parent::display_element($element, $children, $max_depth, $depth, $args, $output);
   }
}

Ini mungkin memberi kita pendekatan alternatif jika berhasil.

birgire
sumber
terima kasih, selalu menarik & menyenangkan untuk melihat berbagai pendekatan penyelesaian masalah - +1 Anda terlihat keren. @ialocin
birgire
1
Sama di sini, tetapi kami sudah tahu siapa yang memberikan suara :) Menjelajahi kemungkinan itu menyenangkan! Sisanya sering seperti pekerjaan perakitan, yaitu ... katakan saja tidak menyenangkan.
Nicolai
Terima kasih, saya berharap akan ada fungsi WP "asli" untuk ini. Saya akan menunggu lebih lama untuk melihat apakah seseorang memposting solusi lain, jika tidak ini akan menjadi jawaban yang dipilih.
YemSalat
Saya memperbarui jawabannya dengan pendekatan lain @YemSalat
birgire
Wah! Itu membuat pikiran saya berputar. Saya tidak pernah berurusan dengan kelas Walker sebelumnya (saya tahu itu ada), saya berharap akan ada cara yang lebih performan dengan beberapa query SQL, tapi saya benar-benar tidak ingin masuk ke struktur WP db. Untuk sekarang saya lebih suka pendekatan pertama Anda di mana bersepeda melalui wp_get_nav_menu_itemssecara rekursif.
YemSalat
3

Singkatnya fungsi di bawah ini memang membuat pohon objek dengan menempatkan anak-anak ke properti anak-anak baru di dalam objek induk.

Kode:

function wpse170033_nav_menu_object_tree( $nav_menu_items_array ) {
    foreach ( $nav_menu_items_array as $key => $value ) {
        $value->children = array();
        $nav_menu_items_array[ $key ] = $value;
    }

    $nav_menu_levels = array();
    $index = 0;
    if ( ! empty( $nav_menu_items_array ) ) do {
        if ( $index == 0 ) {
            foreach ( $nav_menu_items_array as $key => $obj ) {
                if ( $obj->menu_item_parent == 0 ) {
                    $nav_menu_levels[ $index ][] = $obj;
                    unset( $nav_menu_items_array[ $key ] );
                }
            }
        } else {
            foreach ( $nav_menu_items_array as $key => $obj ) {
                if ( in_array( $obj->menu_item_parent, $last_level_ids ) ) {
                    $nav_menu_levels[ $index ][] = $obj;
                    unset( $nav_menu_items_array[ $key ] );
                }
            }
        }
        $last_level_ids = wp_list_pluck( $nav_menu_levels[ $index ], 'db_id' );
        $index++;
    } while ( ! empty( $nav_menu_items_array ) );

    $nav_menu_levels_reverse = array_reverse( $nav_menu_levels );

    $nav_menu_tree_build = array();
    $index = 0;
    if ( ! empty( $nav_menu_levels_reverse ) ) do {
        if ( count( $nav_menu_levels_reverse ) == 1 ) {
            $nav_menu_tree_build = $nav_menu_levels_reverse;
        }
        $current_level = array_shift( $nav_menu_levels_reverse );
        if ( isset( $nav_menu_levels_reverse[ $index ] ) ) {
            $next_level = $nav_menu_levels_reverse[ $index ];
            foreach ( $next_level as $nkey => $nval ) {
                foreach ( $current_level as $ckey => $cval ) {
                    if ( $nval->db_id == $cval->menu_item_parent ) {
                        $nval->children[] = $cval;
                    }
                }
            }
        }
    } while ( ! empty( $nav_menu_levels_reverse ) );

    $nav_menu_object_tree = $nav_menu_tree_build[ 0 ];
    return $nav_menu_object_tree;
}

Pemakaian:

$nav_menu_items = wp_get_nav_menu_items( 'main-menu' );
wpse170033_nav_menu_object_tree( $nav_menu_items );

Keluaran:

Array
(
 [0] => WP_Post Object
  (
   [ID] => 51
   [post_author] => 1
   [post_date] => 2015-06-26 21:13:23
   [post_date_gmt] => 2015-06-26 19:13:23
   [post_content] => 
   [post_title] => 
   [post_excerpt] => 
   [post_status] => publish
   [comment_status] => open
   [ping_status] => open
   [post_password] => 
   [post_name] => 51
   [to_ping] => 
   [pinged] => 
   [post_modified] => 2015-07-29 20:55:10
   [post_modified_gmt] => 2015-07-29 18:55:10
   [post_content_filtered] => 
   [post_parent] => 0
   [guid] => http://example.com/?p=51
   [menu_order] => 1
   [post_type] => nav_menu_item
   [post_mime_type] => 
   [comment_count] => 0
   [filter] => raw
   [db_id] => 51
   [menu_item_parent] => 0
   [object_id] => 2
   [object] => page
   [type] => post_type
   [type_label] => Page
   [url] => http://example.com/example-page/
   [title] => Example-Page-1
   [target] => 
   [attr_title] => 
   [description] => 
   [classes] => Array
    (
     [0] => 
    )
   [xfn] => 
   [children] => Array
    (
     [0] => WP_Post Object
      (
       [ID] => 80
       [post_author] => 1
       [post_date] => 2015-06-27 14:03:31
       [post_date_gmt] => 2015-06-27 12:03:31
       [post_content] => 
       [post_title] => 
       [post_excerpt] => 
       [post_status] => publish
       [comment_status] => open
       [ping_status] => open
       [post_password] => 
       [post_name] => 80
       [to_ping] => 
       [pinged] => 
       [post_modified] => 2015-07-29 20:55:10
       [post_modified_gmt] => 2015-07-29 18:55:10
       [post_content_filtered] => 
       [post_parent] => 2
       [guid] => http://example.com/?p=80
       [menu_order] => 2
       [post_type] => nav_menu_item
       [post_mime_type] => 
       [comment_count] => 0
       [filter] => raw
       [db_id] => 80
       [menu_item_parent] => 51
       [object_id] => 69
       [object] => page
       [type] => post_type
       [type_label] => Page
       [url] => http://example.com/example-page/subpage-1/
       [title] => Subpage-1
       [target] => 
       [attr_title] => 
       [description] => 
       [classes] => Array
        (
         [0] => 
        )
       [xfn] => 
       [children] => Array
        (
        )
      )
    )
  )
 [1] => WP_Post Object
  (
   [ID] => 49
   [post_author] => 1
   [post_date] => 2015-06-26 21:13:23
   [post_date_gmt] => 2015-06-26 19:13:23
   [post_content] => 
   [post_title] => 
   [post_excerpt] => 
   [post_status] => publish
   [comment_status] => open
   [ping_status] => open
   [post_password] => 
   [post_name] => 49
   [to_ping] => 
   [pinged] => 
   [post_modified] => 2015-07-29 20:55:10
   [post_modified_gmt] => 2015-07-29 18:55:10
   [post_content_filtered] => 
   [post_parent] => 0
   [guid] => http://example.com/?p=49
   [menu_order] => 3
   [post_type] => nav_menu_item
   [post_mime_type] => 
   [comment_count] => 0
   [filter] => raw
   [db_id] => 49
   [menu_item_parent] => 0
   [object_id] => 41
   [object] => page
   [type] => post_type
   [type_label] => Page
   [url] => http://example.com/example-page-2/
   [title] => Example-Page-2
   [target] => 
   [attr_title] => 
   [description] => 
   [classes] => Array
    (
     [0] => 
    )
   [xfn] => 
   [children] => Array
    (
    )
  )
 [2] => WP_Post Object
  (
   [ID] => 48
   [post_author] => 1
   [post_date] => 2015-06-26 21:13:23
   [post_date_gmt] => 2015-06-26 19:13:23
   [post_content] => 
   [post_title] => 
   [post_excerpt] => 
   [post_status] => publish
   [comment_status] => open
   [ping_status] => open
   [post_password] => 
   [post_name] => 48
   [to_ping] => 
   [pinged] => 
   [post_modified] => 2015-07-29 20:55:10
   [post_modified_gmt] => 2015-07-29 18:55:10
   [post_content_filtered] => 
   [post_parent] => 0
   [guid] => http://example.com/?p=48
   [menu_order] => 4
   [post_type] => nav_menu_item
   [post_mime_type] => 
   [comment_count] => 0
   [filter] => raw
   [db_id] => 48
   [menu_item_parent] => 0
   [object_id] => 42
   [object] => page
   [type] => post_type
   [type_label] => Page
   [url] => http://example.com/example-page-3/
   [title] => Example-Page-3
   [target] => 
   [attr_title] => 
   [description] => 
   [classes] => Array
    (
     [0] => 
    )
   [xfn] => 
   [children] => Array
    (
     [0] => WP_Post Object
      (
       [ID] => 79
       [post_author] => 1
       [post_date] => 2015-06-27 14:03:31
       [post_date_gmt] => 2015-06-27 12:03:31
       [post_content] => 
       [post_title] => 
       [post_excerpt] => 
       [post_status] => publish
       [comment_status] => open
       [ping_status] => open
       [post_password] => 
       [post_name] => 79
       [to_ping] => 
       [pinged] => 
       [post_modified] => 2015-07-29 20:55:10
       [post_modified_gmt] => 2015-07-29 18:55:10
       [post_content_filtered] => 
       [post_parent] => 42
       [guid] => http://example.com/?p=79
       [menu_order] => 5
       [post_type] => nav_menu_item
       [post_mime_type] => 
       [comment_count] => 0
       [filter] => raw
       [db_id] => 79
       [menu_item_parent] => 48
       [object_id] => 70
       [object] => page
       [type] => post_type
       [type_label] => Page
       [url] => http://example.com/example-page-3/subpage-2/
       [title] => Subpage-2
       [target] => 
       [attr_title] => 
       [description] => 
       [classes] => Array
        (
         [0] => 
        )
       [xfn] => 
       [children] => Array
        (
         [0] => WP_Post Object
          (
           [ID] => 78
           [post_author] => 1
           [post_date] => 2015-06-27 14:03:31
           [post_date_gmt] => 2015-06-27 12:03:31
           [post_content] => 
           [post_title] => 
           [post_excerpt] => 
           [post_status] => publish
           [comment_status] => open
           [ping_status] => open
           [post_password] => 
           [post_name] => 78
           [to_ping] => 
           [pinged] => 
           [post_modified] => 2015-07-29 20:55:10
           [post_modified_gmt] => 2015-07-29 18:55:10
           [post_content_filtered] => 
           [post_parent] => 70
           [guid] => http://example.com/?p=78
           [menu_order] => 6
           [post_type] => nav_menu_item
           [post_mime_type] => 
           [comment_count] => 0
           [filter] => raw
           [db_id] => 78
           [menu_item_parent] => 79
           [object_id] => 76
           [object] => page
           [type] => post_type
           [type_label] => Page
           [url] => http://example.com/example-page-3/subpage-2/subpage-3/
           [title] => Subpage-3
           [target] => 
           [attr_title] => 
           [description] => 
           [classes] => Array
            (
             [0] => 
            )
           [xfn] => 
           [children] => Array
            (
             [0] => WP_Post Object
              (
               [ID] => 87
               [post_author] => 1
               [post_date] => 2015-06-27 15:27:08
               [post_date_gmt] => 2015-06-27 13:27:08
               [post_content] => 
               [post_title] => 
               [post_excerpt] => 
               [post_status] => publish
               [comment_status] => open
               [ping_status] => open
               [post_password] => 
               [post_name] => 87
               [to_ping] => 
               [pinged] => 
               [post_modified] => 2015-07-29 20:55:10
               [post_modified_gmt] => 2015-07-29 18:55:10
               [post_content_filtered] => 
               [post_parent] => 76
               [guid] => http://example.com/?p=87
               [menu_order] => 7
               [post_type] => nav_menu_item
               [post_mime_type] => 
               [comment_count] => 0
               [filter] => raw
               [db_id] => 87
               [menu_item_parent] => 78
               [object_id] => 85
               [object] => page
               [type] => post_type
               [type_label] => Page
               [url] => http://example.com/example-page-3/subpage-2/subpage-3/subpage-4/
               [title] => Subpage-4
               [target] => 
               [attr_title] => 
               [description] => 
               [classes] => Array
                (
                 [0] => 
                )
               [xfn] => 
               [children] => Array
                (
                 [0] => WP_Post Object
                  (
                   [ID] => 366
                   [post_author] => 1
                   [post_date] => 2015-07-29 20:52:46
                   [post_date_gmt] => 2015-07-29 18:52:46
                   [post_content] => 
                   [post_title] => 
                   [post_excerpt] => 
                   [post_status] => publish
                   [comment_status] => open
                   [ping_status] => open
                   [post_password] => 
                   [post_name] => 366
                   [to_ping] => 
                   [pinged] => 
                   [post_modified] => 2015-07-29 20:55:10
                   [post_modified_gmt] => 2015-07-29 18:55:10
                   [post_content_filtered] => 
                   [post_parent] => 85
                   [guid] => http://example.com/?p=366
                   [menu_order] => 8
                   [post_type] => nav_menu_item
                   [post_mime_type] => 
                   [comment_count] => 0
                   [filter] => raw
                   [db_id] => 366
                   [menu_item_parent] => 87
                   [object_id] => 112
                   [object] => page
                   [type] => post_type
                   [type_label] => Page
                   [url] => http://example.com/example-page-3/subpage-2/subpage-3/subpage-4/subpage-5/
                   [title] => Subpage-5
                   [target] => 
                   [attr_title] => 
                   [description] => 
                   [classes] => Array
                    (
                     [0] => 
                    )
                   [xfn] => 
                   [children] => Array
                    (
                    )
                  )
                )
              )
            )
          )
        )
      )
    )
  )
)
Nicolai
sumber
Struktur mirip pohon di WordPress bukan array multidimensi. Ini adalah array objek dengan informasi asal.
Matt van Andel
Sudah mencoba sekitar 10 solusi berbeda untuk masalah ini. Terima kasih atas solusi hebat ini, ia menyimpannya dalam struktur WP Object yang bagus. Ini harus diterima sebenarnya!
Drmzindec
@JohanPretorius Terima kasih dan dengan senang hati. Nah orang mencari hal yang berbeda. Saya menganggap OP menemukan jawaban lain lebih bermanfaat. Semuanya baik.
Nicolai
1

Versi modifikasi dari jawaban yang diterima di mana ia mempertimbangkan menu_orderproperti item menu untuk mempertahankan urutan yang tepat dari susunan flat asli. menu_orderdiatur secara otomatis oleh WordPress sehingga tidak perlu memeriksa apa pun:

function buildTree(array &$flatNav, $parentId = 0) {
    $branch = [];

    foreach ($flatNav as &$navItem) {
      if($navItem->menu_item_parent == $parentId) {
        $children = buildTree($flatNav, $navItem->ID);
        if($children) {
          $navItem->children = $children;
        }

        $branch[$navItem->menu_order] = $navItem;
        unset($navItem);
      }
    }

    return $branch;
}

Pemakaian:

// get navs
$locations = get_nav_menu_locations();

// get menu items by menu name
$flatMainNav = wp_get_nav_menu_items($locations['main']);
$mainNav = buildTree($flatMainNav);
eballeste
sumber
-2

Mungkin ada kesalahpahaman di sini tentang item menu WordPress nav sebagai struktur seperti pohon.

Struktur mirip pohon di WordPress BUKANLAH ARRAY MULTIDIMENSI!

Perhatikan bahwa meskipun array item menu yang dikembalikan rata, itu tetap struktur seperti pohon karena setiap item berisi informasi tentang asal-usulnya (nilai induk adalah 0 jika item tidak memiliki induk - atau id dari item induk jika tidak).

Ketika Anda meneruskan array seperti itu ke Walkerkelas, ia akan melewati hasil dan membuat dua array - satu berisi item tingkat atas, dan lainnya berisi item anak dalam format di $parent_id => array()mana array berisi item menu yang merupakan anak-anak langsung dari elemen tersebut.

Pejalan kaki kemudian loop melalui array item tingkat atas, memproses item itu dan kemudian memeriksa array anak-anak untuk melihat apakah ada anak-anak untuk elemen saat ini, dan memproses masing-masing dengan cara yang sama, secara rekursif.

Jika Anda ingin tahu cara mengubah struktur seperti pohon WordPress menjadi array multidimensi, itu adalah pertanyaan yang sama sekali berbeda (dan bukan secara teknis pertanyaan WordPress). Tetapi informasi yang dikembalikan oleh wp_get_nav_menu_items() adalah struktur seperti pohon ... dan Anda dapat menggunakan Walkerapa adanya untuk menanganinya.

Jika Anda ingin melihat kode persis yang dijalankan kelas Walker WordPress untuk berjalan di array, lihat Walker-> walk () di WordPress Trac dari baris 213-258 . Anda bisa menggunakan kode apa adanya untuk membangun array multidimensi, jika itu yang Anda cari.

Pejalan kaki

WordPress dirancang untuk menggunakan Walkerkelas untuk memproses struktur seperti pohon. Jika Anda hanya merender suatu menu, atau benar-benar hanya membutuhkan hasil akhir, Anda mungkin ingin mempertimbangkan wp_nav_menu()untuk menggunakan untuk mengeluarkan menu Anda ...

Contoh:

wp_nav_menu(array(
    'menu' => 6, // your menu id, name, or slug
    'echo' => true, // set this to false if you want a string back instead
    'walker' => new Your_Walker(),
));

Anda akan memperluas kelas Walker (mis. Your_Walker()) Untuk mendapatkan output apa pun yang Anda butuhkan. Sebagai contoh, lihat entri ini di Codex .

Matt van Andel
sumber
2
Dalam opsi A, $sorted_menu_itemsmasih array "flat" dan output dari opsi B adalah string.
birgire
Saya pikir ada kesalahpahaman tentang bagaimana WordPress mendefinisikan "struktur seperti pohon". wp_get_nav_menu_items()mengembalikan struktur mirip pohon - yaitu, array di mana setiap item berisi data asal. Struktur ini dimaksudkan untuk diberikan dengan Walkerkelas. Jika use-case di sini hanya melibatkan pengubahan array "flat" menjadi array multidimensi berdasarkan data parentage (misal 'post_parent' => 123), pertanyaan ini tidak secara teknis tentang WordPress dan harus dipindahkan ke Stack Overflow.
Matt van Andel
1
Begini, saya tidak peduli apa yang didefinisikan oleh WordPress sebagai "struktur seperti pohon" (Saya pikir sentens ini bahkan tidak masuk akal) Yang saya pedulikan adalah memiliki array multi dimensi, yang dapat saya lakukan sendiri dengan berbagai hal.
YemSalat
Anda TIDAK akan mendapatkan itu sebagai perilaku WordPress default. Seperti yang dinyatakan orang lain, Anda memiliki semua informasi yang Anda butuhkan untuk merestrukturisasi array seperti yang Anda inginkan, dan saya menautkan Anda ke area spesifik di inti WordPress untuk digunakan sebagai referensi. Ini bukan pertanyaan WordPress sebanyak pertanyaan PHP. Anda bisa menggunakan kelas Walker apa adanya, atau Anda bisa menyalin garis yang relevan dari Walker :: walk () seperti yang saya nyatakan, untuk membangun array Anda.
Matt van Andel