菜单项、页面和(分层)分类法都是具有树状结构的数据示例:术语可以有父项、子项和兄弟项。通常我们希望在 HTML 标记中反映这种结构。例如,为了显示菜单,我们希望 HTML 是“顶级”链接的列表,其中包含其子项的嵌套列表,这些链接本身又包含其子项的嵌套列表,依此类推。本教程将指导您完成 WordPress 提供的一个类,这使得生成此标记变得非常简单。
walker 类是一个抽象类,旨在帮助遍历和显示具有分层(或树状)结构的元素。它实际上并没有“做”任何事情(在生成 HTML 的意义上)。任何事情。它只是跟踪树的每个分支:它必须由其他类扩展,这些类告诉它如何处理遇到的每个元素。 WordPress提供了自己的扩展类,例如:
Walker_Nav_Menu
– 用于显示导航菜单的 HTMLWalker_Page
– 用于显示页面列表Walker_Category
– 用于显示分类术语列表。这些类中的每一个都通过简单地指示该类在树的每个元素和级别上输出的内容来扩展 Walker 类。为了揭开这个类的神秘面纱,我们将看看它的主要方法和几个如何使用它的示例。类本身可以在这里找到。
走
walk( $elements, $max_depth)
walker 类通过 walk 方法启动,并且正是该方法在生成 HTML 后返回该 HTML。它接受两个参数:
$max_depth
– 设置我们探索的代数$args
。然后将其传递给类中的其他方法walk 方法挑选出“顶级”元素(没有父元素的元素)并将它们放入一个数组中。其余的子项被放置在第二个数组中,其中键是其父项的 ID(它是一个二维数组,因为一个父项可以有多个子项):
$children_elements = array( '1' => array() //Array of elements corresponding to children of 1, '4' => array() //Array of elements corresponding to children of 4 );
然后依次循环每个父元素并应用方法 display_element
。
Display_Element
display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output )
顾名思义,display_element
负责显示树中的元素。事实上,它调用了几个函数来执行此操作。这些函数在 Walker 类中故意留空,而正是这些函数在扩展类中进行了更改,因为它们决定了返回的实际 HTML。其中包括:
start_lvl
– 返回新关卡开始的 HTML 的函数。对于列表,这将是新“子列表”的开始,因此将负责返回
标记end_lvl
– 当我们完成一个关卡时调用。在导航菜单示例中,该函数负责使用结束列表标记
start_el
– 负责显示我们当前所在元素的函数。对于菜单,这意味着
标签和项目的链接。end_el
– 在显示元素及其所有子元素之后调用的函数。对于我们的菜单示例,这意味着返回结束
那么 display_element
实际上是做什么的?实际上,这就是沃克级所有魔法发生的地方。首先让我们看看它给出了哪些参数:
$element
– 这是我们当前在树上的元素$children_elements
– 所有子元素的数组(不仅仅是上面提到的元素的子元素)。这是 walk
方法中形成的第二个数组,键是父级的 ID。$max_depth
– 我们可以探索多深$depth
– 我们目前的距离有多远$args
– 可选参数(前面提到过)$output
– 到目前为止的 HTML。当我们探索树的更多内容时,就会添加此内容。 display_element
方法首先调用 start_el
负责显示元素。具体如何执行取决于上下文。对于下拉菜单,它可能是 ,对于导航菜单,它可能是
。请注意,还没有结束标记。如果这个元素有子元素,我们需要先显示它们,以便它们嵌套在这个项目中......
接下来它会检查我们所在的当前元素是否有任何子元素以及我们是否尚未达到最大深度。如果是这样,我们通过为每个子级调用 display_element
依次探索每个子级(深度参数递增 1)。这样 display_element
就会递归调用自身,直到到达底部。
假设我们已经到达“底部”(没有子元素或最大深度的元素),那么它会调用 end_el
添加结束标记。 display_element
的当前实例完成,我们回到父级,将 display_element
应用于下一个子级,直到我们处理完它的每个子级。当父级不再有子级时,我们将向上移动到树上,依此类推,直到探索完每个分支。使困惑?他是一个图表,我希望它能澄清一些事情:
使用 Walker 类使得显示自定义层次结构数据变得非常简单。假设您有一个对象数组,其中包含您希望显示列表的“label
”、“parent_id
”和“object_id
”属性。现在可以通过一个非常简单的类轻松完成:
注意:扩展类负责设置在何处查找元素及其父元素的 ID。
class Walker_Simple_Example extends Walker { // Set the properties of the element which give the ID of the current item and its parent var $db_fields = array( 'parent' => 'parent_id', 'id' => 'object_id' ); // Displays start of a level. E.g '<ul>' // @see Walker::start_lvl() function start_lvl(&$output, $depth=0, $args=array()) { $output .= "n<ul>n"; } // Displays end of a level. E.g '</ul>' // @see Walker::end_lvl() function end_lvl(&$output, $depth=0, $args=array()) { $output .= "</ul>n"; } // Displays start of an element. E.g '<li> Item Name' // @see Walker::start_el() function start_el(&$output, $item, $depth=0, $args=array()) { $output. = "<li>".esc_attr($item->label); } // Displays end of an element. E.g '</li>' // @see Walker::end_el() function end_el(&$output, $item, $depth=0, $args=array()) { $output .= "</li>n"; } } $elements=array(); // Array of elements echo Walker_Simple_Example::walk($elements);
您可以扩展 walker 类来更改显示的内容、更改生成的 HTML 甚至阻止显示某些分支。功能例如:
wp_nav_menu
wp_list_pages
wp_list_categories
提供一个选项来指定您自己的自定义 Walker 类 - 允许您通过指定您自己的自定义 Walker 类相对轻松地改变它们的外观。在许多情况下,扩展适当的 walker 扩展实际上比 Walker 类本身更容易。
假设您想要一个与主菜单相关的辅助(子)菜单。这可能采用位于主菜单正下方或侧栏中的链接形式,仅显示当前“顶级页面”的“后代”菜单项。作为上图中的示例,如果我们在“存档”、“作者”或“新闻”子页面上,我们希望显示“存档”下方的所有链接。由于 Walker_Nav_Menu
完成了我们想要的大部分功能,因此我们将扩展该类而不是 Walker 类。这为我们节省了很多精力,因为 Walker_Nav_Menu
向相关链接添加了适当的类('current
'、'current-ancestor
' 等)。我们将扩展 Walker_Nav_Menu
walker 类来稍微改变逻辑,并防止它显示任何顶级链接或“非根”页面的任何后代。
首先,在您的模板文件中,我们将使用 wp_nav_menu()
函数两次,指向相同的主题位置(我将其称为“primary
”)。如果您还没有注册主题位置,您应该阅读本文。无论您使用哪个主题位置,都应该将菜单保存到该位置。我们将显示该菜单两次。首先,无论您希望“顶级”菜单出现在哪里:
wp_nav_menu( array('theme_location'=>'primary','depth' => 1) );
然后,使用自定义步行器,仅显示(相关的)子页面。
wp_nav_menu( array('theme_location'=>'primary','walker' => new SH_Child_Only_Walker(),'depth' => 0) );
首先,我们不想显示顶级父级。回想一下,负责打开 标记和链接的函数是
start_el
,负责关闭 标记的函数是
end_el
。我们只需检查我们是否处于父级别。如果是的话,我们什么也不做。否则,我们继续“正常”并从 Walker_Nav_Menu
类调用该函数。
// Don't print top-level elements function start_el(&$output, $item, $depth=0, $args=array()) { if( 0 == $depth ) return; parent::start_el(&$output, $item, $depth, $args); } function end_el(&$output, $item, $depth=0, $args=array()) { if( 0 == $depth ) return; parent::end_el(&$output, $item, $depth, $args); }
我们扩展了 display_element
。该函数负责沿着分支行进。如果我们位于顶层而不是当前的根链接,我们希望阻止它。要检查我们所在的分支是否为“当前”,我们检查该项目是否具有以下任何类:“current-menu-item
”、“current-menu-parent
”、“current-”菜单-ancestor
'。
// Only follow down one branch function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output ) { // Check if element as a 'current element' class $current_element_markers = array( 'current-menu-item', 'current-menu-parent', 'current-menu-ancestor' ); $current_class = array_intersect( $current_element_markers, $element->classes ); // If element has a 'current' class, it is an ancestor of the current element $ancestor_of_current = !empty($current_class); // If this is a top-level link and not the current, or ancestor of the current menu item - stop here. if ( 0 == $depth && !$ancestor_of_current) return; parent::display_element( $element, &$children_elements, $max_depth, $depth, $args, &$output ); }
我们现在扩展 start_lvl
和 end_lvl
函数。它们负责输出包装关卡的 HTML(在本例中为
标签)。如果我们位于顶层,我们不想显示这些标签(毕竟所有内容都不会显示)。
// Don't wrap the top level function start_lvl(&$output, $depth=0, $args=array()) { if( 0 == $depth ) return; parent::start_lvl(&$output, $depth, $args); } function end_lvl(&$output, $depth=0, $args=array()) { if( 0 == $depth ) return; parent::end_lvl(&$output, $depth, $args); }
该课程的完整内容:
class SH_Child_Only_Walker extends Walker_Nav_Menu { // Don't start the top level function start_lvl(&$output, $depth=0, $args=array()) { if( 0 == $depth ) return; parent::start_lvl(&$output, $depth,$args); } // Don't end the top level function end_lvl(&$output, $depth=0, $args=array()) { if( 0 == $depth ) return; parent::end_lvl(&$output, $depth,$args); } // Don't print top-level elements function start_el(&$output, $item, $depth=0, $args=array()) { if( 0 == $depth ) return; parent::start_el(&$output, $item, $depth, $args); } function end_el(&$output, $item, $depth=0, $args=array()) { if( 0 == $depth ) return; parent::end_el(&$output, $item, $depth, $args); } // Only follow down one branch function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output ) { // Check if element as a 'current element' class $current_element_markers = array( 'current-menu-item', 'current-menu-parent', 'current-menu-ancestor' ); $current_class = array_intersect( $current_element_markers, $element->classes ); // If element has a 'current' class, it is an ancestor of the current element $ancestor_of_current = !empty($current_class); // If this is a top-level link and not the current, or ancestor of the current menu item - stop here. if ( 0 == $depth && !$ancestor_of_current) return parent::display_element( $element, &$children_elements, $max_depth, $depth, $args, &$output ); } }
一旦您了解了 walker 类的工作原理,您就可以扩展它(或 WordPress 的现有扩展)来改变层次结构数据的显示方式。例如,您可以: