워드프레스 테마 만들기: 사이드 네비게이션 (1)

By @soma0sd1/23/2018kr-dev

저번에 만들었던 사이드바 중 왼쪽 사이드바는 네비게이션 역할을 합니다.

네비게이션 워커

이곳의 내용은 Wordpress Reference: Walker를 참고했습니다.

네비게이션은 워드프레스 내장함수인 wp_nav_menu()를 이용해서 불러올 수 있습니다. 하지만 원하는대로 꾸미기에는 무언가 부족한데요.

<?php
wp_nav_menu( array(
  'walker'         => new S0_Walker_Nav(),
) );
?>

워커(walker)라는 것을 따로 지정해서 원하는대로 꾸밀 수 있습니다. 새로운 워커를 만들 때 기존 워커인 Walker_Nav_Menu()를 상속받고 원하는 함수만 덧씌워서 사용할 수 있습니다. 네비게이션과 관련한 함수는 아래의 4가지가 있습니다.

<?php
class S0_Walker_Nav extends Walker_Nav_Menu {
  function start_lvl( &$output, $depth = 0, $args = array() ) {
    // 레벨을 시작할 때 (ul, ol, div 등)
  }
  function end_lvl( &$output, $depth = 0, $args = array() ) {
    // 레벨을 끝낼 때
  }
  function start_el( &$output, $item, $depth=0, $args=array(), $id=0 ) {
    // 요소를 시작할 때
  }
  function end_el( &$output, $object, $depth = 0, $args = array() ) {
    // 요소를 끝낼 때
  }
}
?>

원본의 내용을 확인하고 싶다면 워드프레스 설치 디렉토리에서 /wp-includes/class-walker-nav-menu.php파일을 찾아 열어보면 됩니다.

클래스

네비게이션 요소의 특성은 클래스로 구분됩니다. start_elend_el에서 클래스 목록을 얻을 수 있습니다.

<?php
$classes = empty( $item->classes ) ? array() : (array) $item->classes;
$classes = apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args, $depth );
?>
  • menu-item: 모든 요소에 이 클래스가 들어갑니다.
  • menu-item-object-post: 포스트 요소입니다.
  • menu-item-object-custom: 직접 지정한 하이퍼링크입니다.
  • menu-item-object-page: 페이지 요소입니다.
  • menu-item-object-category: 카테고리 요소입니다.
  • front-item: 최상위 요소입니다
  • sub-item: 하위 요소입니다.
  • menu-item-has-children: 요소에 자식(하위요소)가 있는 경우입니다.
  • current-menu-item: 선택한 요소입니다.
  • current-menu-parent: 선택한 요소의 부모입니다.
  • current-menu-ancestor: 선택한 메뉴의 조상입니다.

구조

네비게이션의 요소와 구조가 다음과 같을 때,

  • 메뉴 1
  • 메뉴 2
    • 메뉴 2-1
    • 메뉴 2-2

네비게이션 워커는 다음과 같은 방식으로 동작합니다.

<?php
/**
* 네비게이션 워커 시작
*/
start_el(...); // 메뉴 1
end_el(...);   // 메뉴 1
start_el(...); // 메뉴 2
  start_lvl(...); // 레벨 2
    start_el(...);  // 메뉴 2-1
    end_el(...);    // 메뉴 2-1
    start_el(...);  // 메뉴 2-2
    end_el(...);    // 메뉴 2-2
  end_lvl(...);   // 레벨 2
end_el(...);    // 메뉴 2
/**
* 네비게이션 워커 끝
*/
?>

예시

<?php
class S0_Walker_Nav extends Walker_Nav_Menu {
}

function.php에 클래스를 하나 만듭니다. 이 클래스는 워드프레스의 내부 클래스인 Walker_Nav_Menu를 상속받습니다.

public function start_lvl( &$output, $depth = 0, $args = array() ) {
  $classes = array( 'sub-menu', "sub-menu-$depth" );
  $class_names = join( ' ', apply_filters( 'nav_menu_submenu_css_class', $classes, $args, $depth ) );
  $class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : '';
  $output .= "<ul$class_names>";
}

public function end_lvl( &$output, $depth = 0, $args = array() ) {
  $output .= "</ul> <!-- sub-menu-$depth -->";
}

S0_Walker_Nav클래스의 레벨 함수입니다. start_lvlend_lvl은 서브메뉴가 만들어 질 때에 작동합니다. 모든 메뉴를 감싸는 요소는 wp_nav_menu()의 컨테이너(container)속성으로 제어할 수 있습니다.

function start_el( &$output, $item, $depth=0, $args=array(), $id=0 ) {

요소를 시작할 때 작동하는 함수인 start_el은 내용이 많아서 기능에 따라 몇 구간으로 나눠서 살펴보겠습니다.

  $args = apply_filters( 'nav_menu_item_args', $args, $item, $depth );

$args는 특정 아이템에 어떤 속성이 있을 때 반영하기 위한 변수입니다.

	$classes = empty( $item->classes ) ? array() : (array) $item->classes;
  $classes = apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args, $depth );
  $classes[] = ( $depth ) ? 'sub-item' : 'front-item';

	$class_names = join( ' ', $classes );
	$class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : '';

	$output .= '<li' . $class_names .'>';

$classes에는 클래스를 배열 형태로 저장합니다. $class_names<li>태그의 클래스 속성을 담은 문자열입니다. 앞서 다룬 클래스들이 $classes에 들어가므로 요소의 종류에 따라 특별한 처리가 필요하다면 이것을 적극적으로 활용하는 것이 좋습니다.

	$atts = array();
	$atts['title']  = ! empty( $item->attr_title ) ? $item->attr_title : '';
	$atts['target'] = ! empty( $item->target )     ? $item->target     : '';
	$atts['rel']    = ! empty( $item->xfn )        ? $item->xfn        : '';
	$atts['href']   = ! empty( $item->url )        ? $item->url        : '';
	$atts = apply_filters( 'nav_menu_link_attributes', $atts, $item, $args, $depth );

	$attributes = '';
	foreach ( $atts as $attr => $value ) {
		if ( ! empty( $value ) ) {
			$value = ( 'href' === $attr ) ? esc_url( $value ) : esc_attr( $value );
			$attributes .= ' ' . $attr . '="' . $value . '"';
		}
	}

메뉴에 접근하기 위한 하이퍼링크 <a>의 속성을 받아오는 부분입니다. $atts에는 키와 값으로 이루어져 있는 배열로 속성이 들어가 있으며, $attributes에는 속성이 문자열로 들어가 있습니다.

	$title = apply_filters( 'the_title', $item->title, $item->ID );

	$title = apply_filters( 'nav_menu_item_title', $title, $item, $args, $depth );

하이퍼링크의 텍스트 부분입니다. 뒤에 <a>$title</a>의 형태로 출력됩니다.

	$item_output = $args->before;
  $item_output .= '<a'. $attributes .'>';
	$item_output .= $args->link_before . $title . $args->link_after;
	$item_output .= '</a>';
	$item_output .= $args->after;

	$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
}

최종 결과를 $output에 추가합니다. $item_output은 요소 전후에 들어갈 내용, 링크텍스트 전후에 들어갈 부분을 속성으로부터 받아오고, 요소의 내용을 채웁니다.

function end_el( &$output, $item, $depth = 0, $args = array() ) {
	$output .= "</li>";
}

end_el은 요소를 닫습니다.

출력

soma0sd.local_.png

전체 코드

functions.php

<?php
register_nav_menus( array(
    'nav-menu' => __( 'Nav Menu' ),
) );

functions.php 혹은 다른 php파일로 만들어 추가합니다.

<?php
class S0_Walker_Nav extends Walker_Nav_Menu {
  public function start_lvl( &$output, $depth = 0, $args = array() ) {
    $classes = array( 'sub-menu', "sub-menu-$depth" );
    $class_names = join( ' ', apply_filters( 'nav_menu_submenu_css_class', $classes, $args, $depth ) );
    $class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : '';
    $output .= "<ul$class_names>";
  }
  public function end_lvl( &$output, $depth = 0, $args = array() ) {
    $output .= "</ul> <!-- sub-menu-$depth -->";
  }

  function start_el( &$output, $item, $depth=0, $args=array(), $id=0 ) {
    $args = apply_filters( 'nav_menu_item_args', $args, $item, $depth );

		$classes = empty( $item->classes ) ? array() : (array) $item->classes;
    $classes = apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args, $depth );
    $classes[] = ( $depth ) ? 'sub-item' : 'front-item';

		$class_names = join( ' ', $classes );
		$class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : '';

		$output .= '<li' . $class_names .'>';

		$atts = array();
		$atts['title']  = ! empty( $item->attr_title ) ? $item->attr_title : '';
		$atts['target'] = ! empty( $item->target )     ? $item->target     : '';
		$atts['rel']    = ! empty( $item->xfn )        ? $item->xfn        : '';
		$atts['href']   = ! empty( $item->url )        ? $item->url        : '';

		$atts = apply_filters( 'nav_menu_link_attributes', $atts, $item, $args, $depth );

		$attributes = '';
		foreach ( $atts as $attr => $value ) {
			if ( ! empty( $value ) ) {
				$value = ( 'href' === $attr ) ? esc_url( $value ) : esc_attr( $value );
				$attributes .= ' ' . $attr . '="' . $value . '"';
			}
		}

		$title = apply_filters( 'the_title', $item->title, $item->ID );

		$title = apply_filters( 'nav_menu_item_title', $title, $item, $args, $depth );

		$item_output = $args->before;
    $item_output .= '<a'. $attributes .'>';
		$item_output .= $args->link_before . $title . $args->link_after;
		$item_output .= '</a>';
		$item_output .= $args->after;

		$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
  }

  function end_el( &$output, $item, $depth = 0, $args = array() ) {
		$output .= "</li>";
	}
}

sidebar.php 원하는 부분에 다음 내용을 추가합니다.

<?php
wp_nav_menu(array(
  'menu'           => 'nav-menu',
  'theme_location' => 'nav-menu',
  'container'      => 'ul',
  'walker'         => new S0_Walker_Nav(),
) );
?>

내용이 길어져서 필요한 몇 가지 기능을 붙이는 작업은 다음에 다루기로 하겠습니다.

4

comments