การรวมประเภทโพสต์ที่กำหนดเองในลำดับชั้นของหน้า


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

โครงสร้างที่สามที่นี่ใช้หน้าเกี่ยวกับและสมาชิกของทีม แต่จะใช้บุ้งชนิดโพสต์ที่กำหนดเองเพื่อให้ดูเหมือนว่าผู้ปกครองเป็นสมาชิกของทีมและเกี่ยวกับ ฉันได้รับมาโดยการตั้งค่าตัวเลือกต่อไปนี้ในประเภทโพสต์ที่กำหนดเอง:

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

วิธีนี้ใช้งานได้ดี แต่เมื่อฉันลงไปที่ระดับโพสต์สมาชิกในทีมฉันจะไม่ได้รับคลาสเพจปัจจุบันและบรรพบุรุษที่เป็นปัจจุบันในเพจระดับบนอีกต่อไป ฉันรู้ว่าทำไมถึงเป็นเช่นนี้เพราะเราไม่ได้ใช้เทคนิคในหน้าหลักของหน้าเว็บเหล่านั้น แต่มีวิธีที่ฉันสามารถหลอกลวง / แก้ไข / bodge ได้หรือไม่ดังนั้นหน้าเว็บจึง apear ในฐานะผู้ปกครอง

ฉันประสบความสำเร็จในเรื่องนี้อย่างมากโดยใช้หน้าเว็บสำหรับสมาชิกในทีมอย่างไรก็ตามมีการเลือกประเภทโพสต์ที่กำหนดเองแทนเพื่อให้ง่ายต่อการใช้งานสำหรับผู้ดูแลระบบ

ขอบคุณพวกคุณผู้หญิง +!


คุณต้องตั้ง id หน้าสมาชิกในทีมเป็นประเภทโพสต์ที่กำหนดเองของคุณ post_parent
Bainternet

ฉันไม่เห็นตัวเลือกนั้นในregister_post_typeเอกสารคุณสามารถช่วยได้ไหม
Ben Everard

คำตอบ:


6

เมื่อทำงานกับเพจคุณสามารถเลือกเพจระดับบนและค่าดังกล่าวจะถูกบันทึกเป็นหมายเลขรหัสของเพจระดับบนpost_parentในฟิลด์ของเพจย่อยในฐานข้อมูล

ในกรณีของคุณคุณใช้ประเภทโพสต์ที่กำหนดเองดังนั้นคุณจะต้องสร้าง metabox ของคุณเองสำหรับหน้าหลัก; สิ่งที่ต้องการ:

/* 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
Righto ดังนั้นฉันสามารถดูวิธีการนี้ "คนโง่" WordPress จะคิดว่าหน้าเฉพาะมันเป็นของผู้ปกครอง wp_list_pagesแต่ก็ไม่ได้เพิ่มระดับผู้ปกครองหน้าไปยังหน้าแม่เมื่อฉัน
Ben Everard

1
ฉันได้สังเกตเห็นนี้ยังยุ่งกับโครงสร้างทาก / Permalink ของฉัน ... : S
Ben Everard

2
ฉันพยายามบรรลุสิ่งเดียวกับเบ็น แต่ฉันใช้wp_nav_menu- post_parent เป็นเรื่องเกี่ยวกับ / สมาชิกในทีม แต่การนำทางเน้นรายการหลักของโพสต์บล็อก "ปกติ" ของฉัน ... ความคิดอื่น ๆ ที่ฉันสามารถแก้ไขได้อย่างไร
pkyeck

@BenEverard: คุณพบวิธีแก้ปัญหาโครงสร้างความยุ่งเหยิงหรือไม่?
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 ของฉันเท่านั้น ไม่พบตัวติดตามข้อผิดพลาดตามที่ระบุ


ฉันได้รวบรวมปลั๊กอินเล็กน้อยเพื่อทดสอบสิ่งนี้ซึ่งอาจช่วยใครบางคน แต่เหมือนที่ฉันกล่าวไว้ข้างต้นข้อจำกัดความรับผิดชอบฉันไม่สามารถสร้างปัญหาในการติดตั้ง WordPress ปัจจุบัน ฉันได้แยกปลั๊กอินออกเป็นสี่ไฟล์พวกเขาจะรวมกันเป็นหนึ่งไดเรกทอรีในไดเรกทอรีปลั๊กอิน

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 prgrammatically

นี่คือขั้นตอน:

  1. คุณสามารถใช้เบ็ด save_postเพื่อ 'จับ' เมื่อใดก็ตามที่มีคนพยายามบันทึกโพสต์
  2. หากโพสต์นั้นเป็นประเภทโพสต์ที่กำหนดเองที่คุณอยู่หลังจากนั้นให้ดำเนินการต่อ
  3. ตรวจสอบให้แน่ใจว่าตั้งค่าโพสต์หลักในเพจที่คุณต้องการ (คุณสามารถกำหนดรหัสหน้าได้ยากตราบใดที่คุณไม่ได้ลบ) คุณสามารถใช้wp_update_postเพื่อบันทึกผู้ปกครอง (ฉันยังไม่ได้ลองตัวเอง แต่ฉันไม่เห็นว่าทำไมมันถึงไม่ทำงาน)

ฉันอยากจะเห็นรหัสนี้มาก! นี้จะสมบูรณ์แบบ แต่ฉันไม่สามารถทำให้มันทำงาน mysef
Johan Dahl

0

ฉันมีเวลาอีกมากที่จะขุดลงไปในสิ่งนี้ด้วยตัวเอง (ขออภัยถ้าฉันเสียเวลาไปกับใคร) และฉันคิดว่าสำหรับฉันวิธีที่ดีที่สุดในการแก้ปัญหาที่เน้น_wp_menu_item_classes_by_context()คือการทำสิ่งที่กำลังทำซ้ำอีกครั้งparent และบรรพบุรุษของรายการเมนูที่ทำหน้าที่เป็นพาเรนต์ของประเภทโพสต์ที่กำหนดเองของฉันและเพิ่มคลาสอย่างเหมาะสม

เนื่องจากฉันต้องการให้เพจระดับบนสำหรับประเภทโพสต์ที่กำหนดเองของฉันได้รับการแก้ไขและเปลี่ยนแปลงได้ง่ายโดยไม่ต้องอัปเดตโพสต์ทั้งหมดเมื่อมีการเปลี่ยนแปลงพาเรนต์ฉันตัดสินใจใช้ตัวเลือกแทนการเติมpost_parentฟิลด์ของโพสต์ประเภทโพสต์แบบกำหนดเอง ฉันใช้ ACF สำหรับสิ่งนั้นเนื่องจากฉันใช้มันในธีมของฉันอย่างไรก็ตามการใช้ฟังก์ชันตัวเลือกเริ่มต้นของ WordPress ก็แน่นอนว่าจะทำเช่นกัน

สำหรับความต้องการของฉันฉันสามารถใช้ประโยชน์จากwp_nav_menu_objectsตัวกรอง นอกจากนี้ฉันต้องกรองpage_for_postsตัวเลือกเพื่อให้ส่งคืนค่าเท็จ / ว่างซึ่งจะเป็นการหลีกเลี่ยงหน้าโพสต์เริ่มต้นที่จะถูกเน้นด้วย

โปรดทราบว่าฉันไม่ได้ไปตลอดทางตัวกรองเพียงเพิ่มcurrent-menu-ancestorและcurrent-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 ) แทนที่จะใช้ตัวเลือกการดึงข้อมูล 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 ของฉันฉันไม่สามารถทำซ้ำได้ การเน้นรายการเมนูที่ถูกต้องใช้งานได้นอกกรอบ
นิโคไล

อ๋อมันยังคงเป็นปัญหาอย่างแน่นอน @ialocin เป็นไปได้ไหมว่าคุณกำลังทดสอบสิ่งนี้ด้วยเมนูระดับ 0 และประเภทโพสต์เริ่มต้น?
ndm

ไม่ลองด้วยรหัสที่โพสต์ในคำตอบของฉัน ดังนั้นด้วยประเภทโพสต์ที่กำหนดเองและเป็นรายการเมนูระดับที่ 1 และ 2 ไปยังหน้าจากประเภทโพสต์ตาม ฉันใช้เวิร์ดเพรสคอร์ธีมที่รวมมาเพื่อทดสอบ
Nicolai

@ialocin ไม่แน่ใจว่าฉันเข้าใจคุณถูกต้องหรือไม่เพราะ " ลองด้วยรหัสที่โพสต์ " และ " ออกจากกล่อง " เป็นเรื่องที่ไม่เหมือนกันใช่ไหม? ;) คุณอ้างอิงถึงประเภทโพสต์ที่กำหนดเองเท่านั้นไม่ใช่การเน้นแก้ไขหรือไม่
ndm

ถูกต้อง :) ตกลงเพื่อความแม่นยำสำหรับสถานการณ์ที่จำเป็นต้องใช้ CPT ดังนั้นแน่นอนว่าฉันลงทะเบียนแล้ว การไฮไลต์ใช้งานได้โดยไม่ต้องใช้กล่องเมตาและการแก้ไขไฮไลต์ ตัวอย่างเช่นโครงสร้างเมนู: grandparent (หน้า)> parent (หน้า)> บางสิ่ง (โพสต์)> สิ่งอื่น (CPT)> อีกสิ่งหนึ่ง (CPT) - ทุกองค์ประกอบจะได้รับคลาส CSS ที่ถูกต้อง; ชุดรูปแบบที่ใช้ที่นี่ยี่สิบสาม
นิโคไล

-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.