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

in #kr-dev7 years ago

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

네비게이션 워커

이곳의 내용은 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> (html comment removed:  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> (html comment removed:  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(),
) );
?>

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

Sort:  

안녕하세요.
워드프레스 개발이라니 멋지십니다.
팔로우 할게요 ㅎㅎ
저는 워드프레스 플러그인를 만들고 싶어서
기존 플러그인 분석하고 수정해서 만들고싶은걸 만들고있습니다.

하다가 막힌부분이 있는데
Php 안에서 javascript 함수를 실행하고 싶은데
워드프레스에서 제공하는 기능이 있던데..
혹시 사용해보신적 있나요?

이쪽은 초보라서 아직 그 스킬이 필요한 상황을 못 겪었네요. 혹시 Ajax를 이용해서 php문서를 불러와야 하는 상황인가요?

아 php 안에서 써야 하는군요. echo로 스크립트를 출력하거나

?>
<script>{스크립트}</script>
<?php

이런식으로 출력하면 될듯 합니다. 자바스크립트 함수의 리턴 값을 이용해야 할 상황이라면 저는 해당 기능을 php로 구현하는 쪽을 선호하는지라..

Hi! I am a robot. I just upvoted you! I found similar content that readers might be interested in:
https://developer.wordpress.org/reference/classes/walker_nav_menu/start_el/