맞춤 게시물 유형을 페이지 계층에 통합


14

팀 구성원을 위해 사용자 정의 게시물 유형으로 테마를 작성 중이며 다음과 같은 페이지 구조도 있습니다.

about  <-- this is a page
about/team-members  <-- this is a page, lists all the team members
about/team-members/joe-bloggs  <-- this is a custom post type (team member) entry

여기에서 세 번째 구조는 about 및 팀 구성원 페이지를 사용하지만 계속해서 사용자 정의 게시물 유형 슬러그를 사용하여 부모가 팀 구성원 인 것처럼 보이도록합니다. 맞춤 게시물 유형에 다음 옵션을 설정하여이를 달성했습니다.

...
'rewrite' => array( 'slug' => 'about/team-members', 'with_front' => false)
...

이것은 잘 작동하지만 팀 구성원 포스트 레벨로 내려갈 때 더 이상 상위 페이지에서 현재 페이지, 현재 조상 클래스를 얻지 못합니다. 기술적으로 해당 페이지의 부모 페이지에 없기 때문에 이것이 왜 그런지 알고 있습니다. 그러나 페이지를 부모로 속일 수 있도록 속임수 / 수정 / 보딩 방법이 있습니까?

팀 구성원을위한 페이지를 사용하여이를 훌륭하게 달성했지만 관리자가 쉽게 사용할 수 있도록 사용자 지정 게시물 유형을 선택했습니다.

고마워 남자 + 여자!


팀원 페이지 ID를 사용자 정의 게시물 유형 post_parent로 설정해야합니다.
Bainternet

register_post_type설명서에 해당 옵션이 표시되지 않습니다. 도와 드릴까요?
벤 에버 라드

답변:


6

페이지 작업시 상위 페이지를 선택할 수 있으며 해당 값은 post_parent데이터베이스 의 하위 페이지 필드에 상위 페이지 ID 번호로 저장됩니다 .

귀하의 경우, 사용자 정의 게시물 유형을 사용하고 있으므로 상위 페이지에 대한 자체 메타 박스를 작성해야합니다. 같은 :

/* Define the custom box */
add_action('add_meta_boxes', 'child_cpt_add_custom_box');

/* Adds a box to the main column on the custom post type edit screens */
function child_cpt_add_custom_box() {
    add_meta_box('child_cpt', __( 'My child_cpt parent'),'team_member_inner_custom_box','team_member');
}

/* Prints the box content */
function team_member_inner_custom_box() {
    global $post;
    // Use nonce for verification
    wp_nonce_field( plugin_basename(__FILE__), 'team_member_inner_custom_box' );
    echo 'Select the parent page';
    $mypages = get_pages();
    echo '<select name="cpt_parent">';
    foreach($mypages as $page){     
        echo '<option value="'.$page->ID.'"';
        if ($page->ID == $post->post_parent) {echo ' selected';}
        echo '>"'.$page->post_title.'</option>';
    }
    echo '</select>';
}
/* Do something with the data entered */
add_action('wp_insert_post_data', 'myplugin_save_postdata');

/* When the post is saved, saves our custom data */
function myplugin_save_postdata( $data, $postarr ) {
    global $post;
      // verify this came from the our screen and with proper authorization,
      // because save_post can be triggered at other times

      if ( !wp_verify_nonce( $_POST['team_member_inner_custom_box'], plugin_basename(__FILE__) ) )
          return $data;

      // verify if this is an auto save routine. 
      // If it is our form has not been submitted, so we dont want to do anything
      if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE ) 
          return $data;
      // OK, we're authenticated: we need to find and save the data

      if ($post->post_type == "team_member")
          $data['post_parent'] = $_POST['cpt_parent'];

     return $data;
}

와는 아무런 관련이 없습니다 register_post_type. WordPress가 다른 게시물 유형 (페이지)의 자식 페이지라고 생각하도록 속입니다.


1
맞아, 그래서 나는 이것이 특정 페이지가 부모라고 생각하기 위해 WordPress가 어떻게 "푸들"하는지 알 수 있지만, I 일 때 부모 페이지에 페이지 부모 클래스를 추가하지 않습니다 wp_list_pages.
벤 에버 라드

1
나는 이것이 또한 내 슬러그 / 퍼머 링크 구조로 혼란스러워하는 것을 보았습니다 ... : S
Ben Everard

2
나는 Ben과 같은 것을 달성하려고 노력하고 있지만 사용 wp_nav_menu합니다. post_parent는 / 팀원이지만 탐색은 내 "정상"블로그 게시물의 상위 항목을 강조 표시합니다 ... 어떻게 해결할 수 있습니까?
pkyeck

@ BenEverard : 당신은 permalink 구조 혼란에 대한 해결책을 찾았습니까?
abaumg

0

나는 비슷한 것을 달성하기 위해 커스텀 워커와 함께 갔다 ... 커스텀 필드가 필요하지 않지만 유형의 모든 게시물은 페이지 트리에서 같은 지점 아래에 앉아 있어야합니다.

class Walker_Page_CustomPostTypeHack extends Walker_Page {
    function walk($elements, $max_depth) {
        $called_with = func_get_args();
        // current page is arg 3... see walk_page_tree for why
        $current_page = $called_with[3];

        // if there's no parent - see if we can find one.
        // some ACF options would be an easy way to make this configurable instad of constants
        if ($current_page === 0) {
            global $wp_query;
            $current_post = $wp_query->get_queried_object();
            switch ($current_post->post_type) {
                case 'course':
                    $current_page = POST_COURSES;
                    break;
                case 'project':
                    $current_page = POST_PROJECTS;
                    break;
                case 'story':
                    $current_page = POST_STORIES;
                    break;
            }
        }

        // now pass on into parent
        $called_with[3] = $current_page;
        return call_user_func_array(array('parent', 'walk'), $called_with);
    }

}

0

면책 조항 : 시도해 본 후에는 더 이상 기존 문제가 아닌 것 같습니다. 적어도 저에게는 WP 3.9.2 설치에서만 작동하기 때문입니다. 그래도 해당 버그 추적기를 찾을 수 없습니다.


나는 이것을 테스트하기 위해 작은 플러그인을 가지고 있는데, 누군가를 도울 수 있습니다. 그러나 위의 면책 조항에서 말했듯이 현재 워드 프레스 설치에서 문제를 재현 할 수 없었습니다. 플러그인을 4 개의 파일로 분리했으며 플러그인 디렉토리 내의 하나의 디렉토리로 함께 가고 있습니다.

plugin-cpt_menu_hierarchy.php :

<?php
defined( 'ABSPATH' ) OR exit;
/**
 * Plugin Name: CPT Menu Hierarchy Fix?
 * Description: CPT Menu Hierarchy Fix?
 * Author:      ialocin
 * Author URL:  http://wordpress.stackexchange.com/users/22534/ialocin
 * Plugin URL:  http://wordpress.stackexchange.com/q/13308/22534
 */

// registering nonsense post type
include 'include-register_post_type.php';

// adding meta box to nosense custom post type
include 'include-cpt_parent_meta_box.php';

// menu highlighting fix
include 'include-menu_highlighting.php';

include-register_post_type.php :

<?php
defined( 'ABSPATH' ) OR exit;

// See: http://codex.wordpress.org/Function_Reference/register_post_type
add_action( 'init', 'wpse13308_basic_reigister_post_type');
function wpse13308_basic_reigister_post_type() {
    $args = array(
        'public' => true,
        'label'  => 'Nonsense'
    );
    register_post_type( 'nonsense', $args );
}

include-cpt_parent_meta_box.php :

<?php
defined( 'ABSPATH' ) OR exit;

// pretty much like @bainternet's answer

// Add Meta Box
add_action( 'add_meta_boxes', 'nonsense_add_meta_box' );
function nonsense_add_meta_box() {
    add_meta_box(
        'nonsense',
        __( 'Nonsense parent' ),
        'nonsense_inner_meta_box',
        'nonsense'
    );
}

// Meta Box Content
function nonsense_inner_meta_box() {
    global $post;

    wp_nonce_field(
        plugin_basename( __FILE__ ),
        'nonsense_inner_meta_box'
    );
    echo 'Parent Page:&nbsp;&nbsp;';
    $mypages = get_pages();
    echo '<select name="cpt_parent">';
    foreach($mypages as $page){     
        echo '<option value="'.$page->ID.'"';
        if ($page->ID == $post->post_parent) {echo ' selected';}
        echo '>'.$page->post_title.'</option>';
    }
    echo '</select>';
}

// Save Data From Meta Box
add_action( 'wp_insert_post_data', 'nonsense_save_meta_box_data' );
function nonsense_save_meta_box_data( $data, $postarr ) {
    global $post;

    if (
        ! wp_verify_nonce(
            $_POST['nonsense_inner_meta_box'],
            plugin_basename( __FILE__ )
        )
    ) {
        return $data;
    }

    if (
        defined('DOING_AUTOSAVE')
        && DOING_AUTOSAVE
    ) {
        return $data;
    }

    if ( $post->post_type == 'nonsense' ) {
        $data['post_parent'] = $_POST['cpt_parent'];
    }
    return $data;
}

include-menu_highlighting.php :

<?php
defined( 'ABSPATH' ) OR exit;

// altering WordPress' nav menu classes via »nav_menu_css_class« filter
add_filter( 'nav_menu_css_class', 'wpse13308_fix_nav_menu_highlighting', 10, 2 );
function wpse13308_fix_nav_menu_highlighting( $classes, $item ) {
    // data of the current post
    global $post;

    // setting up some data from the current post
    $current_post_post_type = $post->post_type;
    $current_post_parent_id = $post->post_parent;
    // id of the post the current menu item represents
    $current_menu_item_id   = $item->object_id;

    // do this for a certain post type
    if( $current_post_post_type == 'nonsense' ) {
        // remove unwanted highlighting class via array_filter and callback
        // http://php.net/manual/de/function.array-filter.php
        $classes = array_filter(
            $classes,
            'wpse13308_remove_highlighting_classes'
        );
        // when the parents id equals the menu items id, we want to
        // highlight the parent menu item, so we check for:
        if( $current_post_parent_id == $current_menu_item_id ) {
            // use the css class used for highlighting
            $classes[] = 'replace-with-css-class';
        }
    }
    return $classes;
}

// callback to remove highlighting classes
function wpse13308_remove_highlighting_classes( $class ) {
    return
        (
            // use the class(es) you need, overview over nav menu item css classes:
            // http://codex.wordpress.org/Function_Reference/wp_nav_menu#Menu_Item_CSS_Classes
            $class == 'highlight-class'
            // uncomment next line if you want to check for more then one class
            // repeat the line if you want to check for a third, fourth and so on
            // || $class == 'replace-with-css-class'
        ) 
        ? false
        : true
    ;
}



  • 이것은 다소 일반화 된 코드 예제입니다.
  • 실제 사용 사례에 맞아야합니다.

0

가능한 해결책은 사용자 정의 게시물 유형이 저장 될 때마다 해당 부모를 about/team-members실용적으로 설정할 수 있습니다 .

단계는 다음과 같습니다.

  1. 누군가가 게시물을 저장하려고 할 때마다 save_post 후크 를 사용하여 'catch'할 수 있습니다 .
  2. 해당 게시물이 사용자 정의 게시물 유형 인 경우 계속 진행하십시오.
  3. 사용자 정의 게시물의 부모를 원하는 페이지로 설정하십시오 (페이지 ID를 삭제하지 않는 한 하드 코딩 할 수 있습니다). wp_update_post 를 사용하여 부모를 저장할 수 있습니다 (직접 시도하지는 않았지만 왜 작동하지 않아야하는지 알 수 없습니다).

이것에 대한 코드를보고 싶습니다! 이것은 완벽하지만 mysef를 작동시킬 수는 없습니다.
Johan Dahl

0

나는 이것을 스스로 파헤칠 시간이 더 있었고 (누군가의 시간을 낭비하면 미안하다), 나는 강조 문제를 해결하는 가장 좋은 방법은 무엇 _wp_menu_item_classes_by_context()을하고 있는지 다시하는 것이라 생각했다. 내 맞춤 게시물 유형의 부모 역할을하는 메뉴 항목의 부모 및 조상 및 클래스를 적절하게 추가합니다.

또한 사용자 정의 게시물 유형의 상위 페이지를 수정하고 부모가 변경되면 모든 게시물을 업데이트하지 않고도 쉽게 변경할 수 있기를 원했기 때문에 사용자 정의 게시물 유형 게시물의 post_parent필드를 채우는 대신 옵션을 사용하기로 결정했습니다 . 어쨌든 내 테마에서 ACF를 사용하고 있기 때문에 ACF를 사용했지만 기본 WordPress 옵션 기능을 사용하는 것도 물론 그렇게합니다.

내 필요에 따라 wp_nav_menu_objects필터를 사용할 수 있습니다 . 또한 옵션필터링하여page_for_posts 잘못된 값 / 빈 값을 반환해야하므로 기본 게시물 페이지가 강조 표시되지 않습니다.

내가 끝까지 가지 않았다는 점에 유의하십시오. 필터는 클래스 current-menu-ancestorcurrent-menu-parent클래스 만 추가 합니다.

/**
 * Filters the `page_for_posts` option on specific custom post types in
 * order to avoid the wrong menu item being marked as
 * `current-page-parent`.
 *
 * @see _wp_menu_item_classes_by_context()
 */
function wpse13308_pre_option_page_for_posts_filter()
{
    $types = array
    (
        'my_custom_post_type_x',
        'my_custom_post_type_y',
        'my_custom_post_type_z'
    );
    if(in_array(get_post_type(), $types))
    {
        return 0;
    }
    return false;
}
add_filter('pre_option_page_for_posts', 'wpse13308_pre_option_page_for_posts_filter');


/**
 * Returns the current posts parent page ID
 *
 * @return int
 */
function wpse13308_get_parent_page_id()
{
    $postType = get_post_type();
    $parentPageId = null;
    switch($postType)
    {
        case 'my_custom_post_type_x':
        case 'my_custom_post_type_y':
        case 'my_custom_post_type_z':
            $parentPageId = (int)get_field('page_for_' . $postType, 'options')->ID;
            break;

        case 'post':
            $parentPageId = (int)get_option('page_for_posts');
            break;
    }
    return $parentPageId;
}

/**
 * Adds proper context based classes so that the parent menu items are
 * being highlighted properly for custom post types and regular posts.
 *
 * @param array $menuItems
 * @return array
 *
 * @see _wp_menu_item_classes_by_context()
 */
function wpse13308_wp_nav_menu_objects_filter(array $menuItems)
{
    $parentPageId = wpse13308_get_parent_page_id();

    if($parentPageId !== null)
    {
        $activeAncestorItemIds = array();
        $activeParentItemIds = array();
        foreach($menuItems as $menuItem)
        {
            if((int)$parentPageId === (int)$menuItem->object_id)
            {
                $ancestorId = (int)$menuItem->db_id;

                while
                (
                    ($ancestorId = (int)get_post_meta($ancestorId, '_menu_item_menu_item_parent', true)) &&
                    !in_array($ancestorId, $activeAncestorItemIds)
                )
                {
                    $activeAncestorItemIds[] = $ancestorId;
                }
                $activeParentItemIds[] = (int)$menuItem->db_id;
            }
        }
        $activeAncestorItemIds = array_filter(array_unique($activeAncestorItemIds));
        $activeParentItemIds = array_filter(array_unique($activeParentItemIds));

        foreach($menuItems as $key => $menuItem)
        {
            $classes = $menuItems[$key]->classes;
            if(in_array(intval($menuItem->db_id), $activeAncestorItemIds))
            {
                $classes[] = 'current-menu-ancestor';
                $menuItems[$key]->current_item_ancestor = true;
            }

            if(in_array($menuItem->db_id, $activeParentItemIds))
            {
                $classes[] = 'current-menu-parent';
                $menuItems[$key]->current_item_parent = true;
            }

            $menuItems[$key]->classes = array_unique($classes);
        }
    }

    return $menuItems;
}
add_filter('wp_nav_menu_objects', 'wpse13308_wp_nav_menu_objects_filter');

완전성을 위해 옵션을 사용하는 대신 채우기post_parent ( @ Bainternet 's answer 참조 )를 수행하면 부모 ID를 검색하면 다음과 같이 보일 수 있습니다.

/**
 * Returns the current posts parent page ID
 *
 * @return int
 */
function wpse13308_get_parent_page_id()
{
    $parentPageId = null;
    $post = get_post();
    switch($post->post_type)
    {
        case 'my_custom_post_type_x':
        case 'my_custom_post_type_y':
        case 'my_custom_post_type_z':
            $parentPageId = (int)$post->post_parent;
            break;

        case 'post':
            $parentPageId = (int)get_option('page_for_posts');
            break;
    }
    return $parentPageId;
}

당신은 내 시간을 낭비하지 않았습니다 :) 또 다른 것은, 이것이 여전히 문제인지 확신합니까? 내 WP 3.9.2 설치에서 재생할 수 없었습니다. 올바른 메뉴 항목을 강조 표시하면 바로 사용할 수 있습니다.
Nicolai

그렇습니다, 그것은 여전히 ​​@ialocin의 문제입니다. 0 레벨 메뉴와 기본 게시물 유형으로 이것을 테스트하고 있습니까?
ndm

아니요, 내 답변에 게시 된 코드로 시도했습니다. 따라서 사용자 정의 게시물 유형과 첫 번째 및 두 번째 수준 메뉴 항목으로 해당 게시물 유형에서 페이지로. 워드 프레스 코어 번들 테마를 사용하여 테스트했습니다.
Nicolai

@ialocin 내가 올바로 이해했는지 확실하지 않습니다. " 포스트 된 코드를 사용해 보았습니다 "및 " 출시 즉시 "는 상호 배타적 이므로 ? ;) 강조 표시 수정이 아닌 맞춤 게시물 유형 만 참조하고 있습니까?
ndm

맞습니다.) 정확히, 시나리오를 위해 CPT가 필요합니다. 물론 등록했습니다. 강조 표시는 메타 상자 및 강조 표시 수정을 사용하지 않고 작동합니다. 예를 들어 메뉴 구조 : 조부모 (페이지)> 부모 (페이지)> 무언가 (게시물)> 다른 것 (cpt)> 하나 이상의 것 (cpt)-모든 요소는 올바른 CSS 클래스를 얻습니다. 여기서 스물 13 개를 사용했습니다.
Nicolai

-1
<?php
the_post();

// $postType holds all the information of the post type of the current post you are viewing
$postType = get_post_type_object(get_post_type());

// $postSlug is the slug you defined in the rewrite column: about/team-members
$postSlug = $postType->rewrite['slug'];

// $datas = { [0] => 'about', [1] => 'team-members' }
$datas = explode('/', $postSlug);

// $pageSlug = 'about'
$pageSlug = $datas[0];

// all the page information you require.
$page = get_page_by_path($pageSlug, OBJECT, 'page');
?>

http://codex.wordpress.org/Function_Reference/get_post_type_object http://codex.wordpress.org/Function_Reference/get_page_by_path

편집 1 :

포인터가 작동하지 않기 때문에 :

add_filter('wp_nav_menu_objects', 'my_menu_class_edit');
function my_menu_class_edit($items)
{
    if (is_single()) {
        $postType = get_post_type_object(get_post_type());
        $postSlug = $postType->rewrite['slug'];
        if($postSlug  != 'about/team-members')
            return $items;
        $datas = explode('/', $postSlug);
        $pageAbout = get_page_by_path($datas[0], OBJECT, 'page');
        $pageTeamMembers = get_page_by_path($datas[1], OBJECT, 'page');

        foreach ($items as $item) {
            if ($item->title == $pageAbout->post_title) {
                $item->classes[] = 'current-ancestor';
            } else if ($item->title == $pageTeamMembers->post_title) {
                $item->classes[] = 'current-page';
            }
        }
   }
    return $items;
}

당신은 간다. wp_nav_menu_objects 필터 후크 내에 추가했습니다.
aifrim
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.