将自定义帖子类型集成到页面层次结构中

时间:2011-03-28 作者:Ben Everard

我正在为团队成员创建一个带有自定义帖子类型的主题,我还有以下页面结构:

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和team member页面,但接着使用了自定义的post类型slug,使其看起来像它的父级是team member和about。我通过在自定义帖子类型上设置以下选项来实现这一点:

...
\'rewrite\' => array( \'slug\' => \'about/team-members\', \'with_front\' => false)
...
这非常有效,但是当我进入团队成员职位级别时,我不再在父页面上获得当前页面、当前祖先类。我知道这是为什么,因为从技术上讲,我们并不是这些页面的家长,但是有没有办法让这些页面成为家长?

通过为团队成员使用页面,我很好地做到了这一点,但是为了便于管理员使用,选择了自定义的帖子类型。

谢谢男生+女生!

6 个回复
SO网友:Bainternet

使用页面时,可以选择父页面,该值将保存为子页面中的父页面id号post_parent 数据库中的字段。

在您的情况下,您使用的是自定义帖子类型,因此需要为父页面创建自己的元框;类似于:

/* 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,使其认为这是另一种帖子类型(页面)的子页面。

SO网友:benlumley

我和一个定制的步行者一起实现了类似的目标。。。避免了对自定义字段的需要,但同一类型的所有帖子都必须位于页面树中的同一点下方。

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);
    }

}

SO网友:Nicolai Grossherr

免责声明:尝试后,这对我来说似乎不再是一个问题,因为至少对我来说,它只适用于我的WP 3.9.2安装。但找不到相应的bug追踪器。

我有一个小插件来测试这一点,这可能会对某些人有所帮助。但正如我在上面的免责声明中所说,我无法在当前的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
    ;
}

这是一个有点泛化的代码示例

SO网友:ndm

我自己有更多的时间来研究这个问题(如果我浪费了任何人的时间,我很抱歉),我想对我来说,解决突出显示问题的最好方法是重新做_wp_menu_item_classes_by_context() 就是迭代菜单项的所有父项和父项,该菜单项作为自定义帖子类型的父项,并适当地添加类。

由于我还希望固定自定义帖子类型的父页面,并且可以轻松地进行更改,而不必在父页面更改后更新所有帖子,因此我决定使用一个选项,而不是填充post_parent 我的自定义帖子类型帖子的字段。我在我的主题中使用了ACF,但使用默认的WordPress选项功能当然也可以。

为了满足我的需要,我可以利用wp_nav_menu_objects 滤器此外,我不得不filter the page_for_posts option 因此,它会返回一个假/空值,这样就避免了突出显示默认的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;
}

SO网友:Shahar Dekel

一种可能的解决方案是,无论何时保存自定义帖子类型,都可以将其“父项”设置为about/team-members P语法上。

以下是步骤:

  1. 您可以使用save_post hook 每当有人试图保存帖子时,都要“抓住”wp_update_post 为了拯救父母(我自己也没有尝试过,但我不明白为什么它不起作用)

SO网友:aifrim

<?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_objecthttp://codex.wordpress.org/Function_Reference/get_page_by_path

EDIT 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;
}

结束

相关推荐

Tag pages do not show

问题:当我请求一个“标记”页面时,例如/tag/which无标记页面只显示frontpage。类别似乎也是如此我做了什么?我已将此博客从单个站点移动到我的WP3多站点设置(#21)中。我已经尝试了什么?其他20个移动的日志工作正常,因此它不是“通用”或。htacess或wpconfig我尝试过其他主题,比如2010:同样的问题,只是为了确保我刷新了永久链接(if(is\\u tag()){echo“”;}else{echo”“;}“始终显示“无标记”…换句话说,页面本身并不认为它是标记页面,术语的表结构和