カスタム投稿タイプをページ階層に統合する


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

ここの3番目の構造は、aboutおよびteam memberページを使用していますが、カスタムポストタイプのスラッグを使用して、親がチームメンバーおよび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が「だまされている」ことがわかりますが、ページの親クラスを親ページに追加しているわけではありませんwp_list_pages
ベン・エバールド

1
S:私は...私のスラグ/パーマリンク構造でこれも台無しに気づいた
ベンEverard

2
私はBenと同じことを達成しようとしていますが、私は使用していますwp_nav_menu-post_parentはabout / team-membersですが、ナビゲーションは私の「通常の」ブログ投稿の親アイテムを強調しています...これを修正する方法はありますか?
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のインストールで動作するからです。ただし、対応するバグトラッカーが見つかりませんでした。


これをテストするための小さなプラグインを一緒に用意しました。これは誰かを助けるかもしれません。しかし、上記の免責事項で述べたように、現在のワードプレスのインストールでは問題を再現できませんでした。プラグインを4つのファイルに分割しました。これらのファイルはプラグインディレクトリ内の1つのディレクトリにまとめられています。

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を使用して親を保存できます(自分で試したことはありませんが、なぜ機能しないのかわかりません)。

このためのコードを見たいです!これは完璧ですが、mysefで動作させることはできません。
ヨハン・ダール

0

私が(すみません、私は誰の時間を無駄にしている場合)、このに自分自身を掘るためにいくつかのより多くの時間を持っていた、と私は私のために、ハイライト表示の問題を解決する最良の方法はちょっと何を再実行することであろうことを考え出し_wp_menu_item_classes_by_context()すべての反復処理であること、以上やっていますカスタム投稿タイプの親として機能するメニュー項目の親と祖先、およびクラスを適切に追加します。

また、カスタム投稿タイプの親ページを修正し、親が変更された後にすべての投稿を更新することなく簡単に変更できるようにしたかったのでpost_parent、カスタム投稿タイプの投稿のフィールドに入力する代わりにオプションを使用することにしました。テーマでACFを使用しているので、そのためにACFを使用しましたが、デフォルトのWordPressオプション機能を使用することももちろんできます。

必要に応じて、wp_nav_menu_objectsフィルターを使用できます。さらにpage_for_postsオプションフィルタリングして、false / empty値を返すようにする必要がありました。これにより、デフォルトの投稿ページも強調表示されなくなります。

私はすべての方法で行ったわけではないことに注意してください。フィルタは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のインストールでは再現できなかったためです。正しいメニュー項目を強調表示することは、箱から出してすぐに機能しました。
ニコライ14

うん、それは間違いなく@ialocinの問題です。0レベルのメニューとデフォルトの投稿タイプでこれをテストしている可能性がありますか?
ndm 14

いいえ、回答に掲載されているコードで試してみました。したがって、カスタム投稿タイプを使用し、第1レベルおよび第2レベルのメニュー項目として、対応する投稿タイプからページに移動します。Wordpressのコアバンドルテーマを使用してテストしました。
ニコライ14

@ialocin「投稿されたコードで試された」と「箱から出してすぐに」は相互に排他的であるため、私があなたを正しく理解しているかどうかはわかりませんか?;)強調表示の修正ではなく、カスタム投稿タイプのみを参照していますか?
ndm 14

右:)わかりました。正確には、シナリオにはCPTが必要なので、もちろん登録しました。強調表示は、メタボックスと強調表示の修正を使用しなくても機能します。たとえば、メニュー構造の場合:祖父母(ページ)>親(ページ)>何か(投稿)>別のもの(cpt)>もう一つ(cpt)-すべての要素は正しいcssクラスを取得します。ここで使用されているテーマは13です。
ニコライ14

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