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


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

这里的第三个结构使用“关于”和“团队成员”页面,但继续使用自定义帖子类型的“ slug”,使其看起来像其父母是“团队成员”和“关于”。我通过在自定义帖子类型上设置以下选项来实现此目的:

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

这很好用,但是当我降到团队成员职位级别时,我不再在父页面上获得当前页面,当前祖先类。我知道为什么会这样,因为从技术上讲我们不在这些页面的父页面上,但是有没有一种我可以欺骗/修复/标记的方法,因此页面确实可以作为父页面出现?

通过为团队成员使用页面,我已经很好地实现了这一点,但是为了方便管理员使用,选择了自定义帖子类型。

谢谢男孩和女孩!


您需要将团队成员页面ID设置为自定义帖子类型post_parent。
Bainternet 2011年

我在register_post_type文档中没有看到该选项,您能帮忙吗?
本·埃弗拉德

Answers:


6

使用页面时,您可以选择一个父页面,并且该值将另存为子页面的父页面ID号。 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
本·埃弗拉德

1
我注意到这也弄乱了我的子弹/永久链接结构...:S
Ben Everard

2
我正在尝试实现与Ben相同的功能,但我使用的wp_nav_menu是-post_parent是关于/ team-members,但导航突出显示了我的“正常”博客文章的父项...还有其他想法可以解决吗?
pkyeck 2011年

@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语法上的。

步骤如下:

  1. 每当有人尝试保存帖子时,您都可以使用save_post钩子 “捕获”。
  2. 如果该帖子是您所需要的自定义帖子类型,请继续。
  3. 确保将自定义帖子的父级设置为所需的页面(只要不删除页面ID,就可以对其进行硬编码)。您可以使用wp_update_post保存父项(我自己还没有尝试过,但是我不明白为什么它不起作用)。

我非常希望看到一些代码!这将是完美的,但是我无法使其正常工作。
约翰·达尔

0

我还有更多时间自己研究这个问题(对不起,如果我浪费了别人的时间),我发现对我来说,解决突出显示问题的最佳方法是重新做_wp_menu_item_classes_by_context()某件事,即遍历所有内容作为自定义帖子类型的父级的菜单项的父级和祖先,并适当添加类。

由于我还希望固定自定义帖子类型的父页面,并且可以轻松更改,而无需在父代更改后更新所有帖子,因此,我决定使用一个选项,而不是填充post_parent自定义帖子类型帖子的字段。我一直使用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的答案)而不是使用选项,然后检索父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 2014年

是的,@ ialocin肯定还是个问题。可能是您正在使用0级菜单和默认帖子类型进行测试吗?
ndm

不,使用我答案中发布的代码进行了尝试。因此,使用自定义帖子类型以及从相应帖子类型到页面的第一级和第二级菜单项。我使用wordpress核心捆绑主题进行了测试。
Nicolai 2014年

@ialocin不确定我是否理解正确,因为“ 尝试过发布的代码 ”和“ 开箱即用 ”是互斥的吗?;)您是否仅引用自定义帖子类型,而不是突出显示修订?
2014年

对:)好的,确切地说,对于需要CPT的情况,所以我当然注册了它。突出显示无需使用元框和突出显示修复程序即可工作。例如,使用菜单结构:祖父母(页面)>父(页面)>某物(帖子)>另一物(cpt)>一件东西(cpt)-每个元素都获得正确的CSS类;这里使用的主题是二十三。
Nicolai 2014年

-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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.