web-dev-qa-db-ja.com

cPTをページの親として設定する

このように登録されているEventというカスタム投稿タイプがあります。

add_action('init', 'register_custom_post_types');

function register_custom_post_types() {
   $event_capabilities = array(
      'publish_posts' => 'publish_events',
      'edit_posts' => 'edit_events',
      'edit_others_posts' => 'edit_others_event',
      'delete_posts' => 'delete_events',
      'delete_published_posts' => 'delete_published_events',
      'delete_others_posts' => 'delete_others_events',
      'read_private_posts' => 'read_private_events',
      'edit_post' => 'edit_event',
      'delete_post' => 'delete_event',
      'read_post' => 'read_event',
   );
   register_post_type('event',
      array(
        'labels' => array(
            'name' => __( 'Event' )
         ),
        'rewrite' => 'event',
        'public' => true,
        'has_archive' => true,
        'show_ui' => true,
        'menu_position' => 8,
        'capability_type' => array('event', 'events'),
        'capabilities' => $event_capabilities,
        'supports' => array('title', 'thumbnail', 'page-attributes'),
        'map_meta_cap' => true,
        'hierarchical' => true,
      )
   );
}

イベントを通常のページの親として設定できるようにしたいです。言い換えれば、私はすべてのイベントをPage Attributesの下のParent select要素に表示したいと思います。

CPTをページの子として設定する方法についての記事をたくさん読んだことがありますが、その逆はできません。

4
Cyclonecode

私たちがここで取り組もうとしている挑戦は、以下の方法です。

  • 現在の ページ属性 メタボックスを削除または置換しない

  • 階層情報をドロップダウンのままにします。

単一ページの編集画面を表示していて、event parentドロップダウン page投稿タイプを追加したいとします。

ドロップダウンは、wp_dropdown_pages()関数を呼び出すget_pages()関数を使用して表示されます。単一の投稿タイプのみをサポートします。

これをする2つの方法はここにあります:

方法1 - SQLを修正する

これは実験的な方法です、なぜならそれはSQLの修正を扱うからです。

生成されたSQLクエリをget_pages()でフィルタリングする明白な方法がないので、私達は私達のニーズに一般的なqueryフィルタを使うことができます。

ドロップダウンをより使いやすくするために、複数の投稿タイプのタイトルが含まれている場合は、投稿タイプ情報の先頭にlist_pagesフィルタを使用します。

例:

そのため、代わりに次のようなオプションを表示します。

"(no parent)"
"About the Music Hall"
    "History"
    "Location"
"Concerts"
    "Of Monsters and Men"
    "Vienna Philharmonic Orchestra"
"Tickets"

我々が得る:

"(no parent)"
"page - About the Music Hall"
    "page  - History" 
    "page  - Location"
"event - Concerts"
    "event - Of Monsters and Men"
    "event - Vienna Philharmonic Orchestra"
"page - Tickets"

デモプラグイン:

テストのためにプラグインに配置できるコードスニペットは次のとおりです。

/**
 * Add the hierarchical 'event' post type, to the 'page' parent drop-down.
 *
 * @link http://wordpress.stackexchange.com/a/204439/26350
 */ 
is_admin() && add_filter( 'page_attributes_dropdown_pages_args', function( $dropdown_args, $post )
{
     // Only do this for the 'page' post type on the single edit 'page' screen
     if( 'page' === get_current_screen()->id && 'page' === $post->post_type )
     {
        // Modify the options' titles
        add_filter( 'list_pages', 'wpse_add_post_type_info_in_options', 10, 2 );

        // Modify the get_pages() query
        add_filter( 'query', function( $sql )
        {
            // Only run this once
            if( ! did_action( 'wpse_change_cpt' ) )
            {
                do_action( 'wpse_change_cpt' );

                // Adjust the post type part of the query - add the 'event' post type
                $sql = str_replace( 
                    "post_type = 'page' ", 
                    "post_type IN ( 'event', 'page' ) ", 
                    $sql 
                );
            }
            return $sql;
        } );
    }
    return $dropdown_args;
}, 10, 2 );

ここで、

function wpse_add_post_type_info_in_options ( $title, $page )
{
    return $page->post_type . ' - ' . $title;
}

そして少しのクリーンアップ:

add_filter( 'wp_dropdown_pages', function( $output )
{
    if( did_action( 'wpse_change_cpt' ) )
        remove_filter( 'list_pages', 'wpse_add_post_type_info_in_options', 10, 2 );

    return $output;
} );

方法2 - wp_dropdown_pages()だけを使う

ここではwp_dropdown_pages()を2回実行してからそれを1つのドロップダウンにマージします。投稿タイプがpageの場合は1回、投稿タイプがeventの場合はもう一度

/**
 * Add the hierarchical 'event' post type, to the 'page' parent drop-down.
 *
 * @link http://wordpress.stackexchange.com/a/204439/26350
 */ 
is_admin() && add_filter( 'page_attributes_dropdown_pages_args', 'wpse_attributes', 99, 2 );

function wpse_attributes( $dropdown_args, $post )
{
    // Run this filter callback only once
    remove_filter( current_filter(), __FUNCTION__, 99 );

    // Only do this for the 'page' post type on the edit page screen
    if( 'page' === get_current_screen()->id && 'page' === $post->post_type )
    {
        // Modify the input arguments, for the 'event' drop-down
        $modified_args = $dropdown_args;
        $modified_args['post_type'] = 'page';
        $modified_args['show_option_no_change'] = __( '=== Select Events here below: ===' );            
        $modified_args['show_option_none'] = false;

        // Add the 'event' drop-down
        add_filter( 'wp_dropdown_pages', function( $output ) use ( $modified_args )
        {
            // Only run it once
            if( ! did_action( 'wpse_dropdown' ) )
            {
                do_action( 'wpse_dropdown' );

                // Create our second drop-down for events
                $output .= wp_dropdown_pages( $modified_args );

                // Adjust the output, so we only got a single drop-down
                $output = str_replace( 
                    [ "<select name='parent_id' id='parent_id'>", "</select>"],
                    '', 
                    $output 
                );
                $output = "<select name='parent_id' id='parent_id'>" . $output . "</select>";
            }
            return $output;
        } );
    }
    return $dropdown_args;
}

ここでは、2つの階層型ドロップダウンは、空の ===下のSelect Eventsの下にある:=== オプションで区切られています。

例:

"(no parent)"
"About the Music Hall"
    "History"
    "Location"
"Tickets"
"=== Select Events here below: ==="
"Concerts"
    "Of Monsters and Men"
    "Vienna Philharmonic Orchestra"

メインクエリを調整する

omam-info slugを使用して Of Monsters And Men - Info というページを作成し、omam slugを使用してイベントとして Of Monsters And Men を選択したとします。

それからパスは

example.tld/omam/omam-info

しかし、これは404エラーになります。これは、メインクエリに対する\WP_Queryクラス内のget_page_path()チェックが失敗するためです。

if ( '' != $qv['pagename'] ) {
    $this->queried_object = get_page_by_path($qv['pagename']);
    if ( !empty($this->queried_object) )
        $this->queried_object_id = (int) $this->queried_object->ID;
    else
        unset($this->queried_object);

これは、ここでget_page_by_path()pageattachmentの投稿タイプのみをチェックし、eventの投稿タイプはチェックしないためです。残念ながら、それを変更するための明示的なフィルタはありません。

もちろん、上記のようにqueryフィルタを使用することもできますが、別の回避策を試してみましょう。

queried_object_idオブジェクトの割り当てられていないプロパティqueried_object_id\WP_Queryを調整することができます。

/**
 * Support for page slugs with any kind event parent hierarchy
 *
 * @link http://wordpress.stackexchange.com/a/204439/26350
 */
add_action( 'pre_get_posts', function( \WP_Query $q )
{
    if( 
            ! is_admin()               // Front-end only
        &&  $q->is_main_query()        // Target the main query
        &&  $q->get( 'pagename' )      // Check for pagename query variable
        &&  ! $q->get( 'post_type' )   // Target the 'page' post type
        && $page = get_page_by_path( $q->get( 'pagename' ), OBJECT, [ 'page', 'event', 'attachment' ] ) 
    ) {
        if( is_a( $page, '\WP_Post' ) )
        {                
            $q->queried_object_id = $page->ID;
            $q->queried_object = $page;
        }
    }
} );

これはまた、次のような任意の数のイベント親をサポートするはずです。

ecample.tld/event-grandparent/event-parent/event-child/page-slug

注意

edit.php画面の親ドロップダウンには、上で使用したquick_edit_dropdown_pages_argsフィルタの代わりにpage_attributes_dropdown_pages_argsフィルタを使用することができます。

5
birgire

まず、コア ページ属性 メタボックスを削除してから、親投稿タイプを変更する以外はすべてをそのままの状態に保って自分で追加します。これが私のコードです。あなたのevents投稿タイプで動作するように調整されています。コピー/貼り付けの準備をしましたが、エラーが発生した場合はお知らせください。

/* Remove the core Page Attribute Metabox */
function event_remove_pa_meta_boxes() {
  remove_meta_box( 'pageparentdiv', 'page', 'side' );
}
add_action( 'do_meta_boxes', 'event_remove_pa_meta_boxes' );

/* Set the Page Attribute Metabox again*/
function events_pa_meta_box( $post ) {

    add_meta_box(
        'event-select',
        __( 'Page Attributes', 'textdomain' ),
        'events_selectors_box',
        'page',
        'side',
        'core'
    );
}
add_action( 'add_meta_boxes_page', 'events_pa_meta_box' );

/* Recreate the meta box. */
function events_selectors_box( $post ) {

    /* Set Events as Post Parent */
    $parents = get_posts(
        array(
            'post_type'   => 'event', 
            'orderby'     => 'title', 
            'order'       => 'ASC', 
            'numberposts' => -1 
        )
    );

    echo '<p><strong>Parent</strong></p><label class="screen-reader-text" for="parent_id">Parent</label>';
    if ( !empty( $parents ) ) {

        echo '<select id="parent_id" name="parent_id">';

        foreach ( $parents as $parent ) {
            printf( '<option value="%s"%s>%s</option>', esc_attr( $parent->ID ), selected( $parent->ID, $post->post_parent, false ), esc_html( $parent->post_title ) );
        }

        echo '</select>';

    } else {

        echo '<p>Please <a href="' . admin_url( "post-new.php?post_type=event" ) . '">create an event first</a>.</p>';

    }

    /* Page Templates */

    if ( 'page' == $post->post_type && 0 != count( get_page_templates( $post ) ) && get_option( 'page_for_posts' ) != $post->ID ) {

        $template = !empty($post->page_template) ? $post->page_template : false;

        echo '<p><strong>Template</strong></p><label class="screen-reader-text" for="page_template">Page Template</label>';

        echo '<select id="page_template" name="page_template">';

        $default_title = apply_filters( 'default_page_template_title',  __( 'Default Template' ), 'meta-box' );

        echo '<option value="default">' . esc_html( $default_title ) . '</option>'      

            page_template_dropdown($template);

        echo '</select>';

    }

    /* Page Order */
    echo '<p><strong>' . _e('Order') .'</strong></p>';

    echo '<label class="screen-reader-text" for="menu_order">'. _e('Order') . '</label><input name="menu_order" type="text" size="4" id="menu_order" value="'. esc_attr($post->menu_order) .'" />';

    /* Help Paragraph */
    if ( 'page' == $post->post_type && get_current_screen()->get_help_tabs() ) { ?>
        echo '<p>' . _e( 'Need help? Use the Help tab in the upper right of your screen.' ) . '</p>';
    }
}  

編集: ページ属性メタボックスのデフォルトの引数を変更するために利用可能なフィルタがあることに気付いた:

$dropdown_args = array(
    'post_type'        => $post->post_type,
    'exclude_tree'     => $post->ID,
    'selected'         => $post->post_parent,
    'name'             => 'parent_id',
    'show_option_none' => __('(no parent)'),
    'sort_column'      => 'menu_order, post_title',
    'echo'             => 0,
);

$dropdown_args = apply_filters( 'page_attributes_dropdown_pages_args', $dropdown_args, $post );  

だから、あなたが望むことをするための超簡単な方法があるかもしれません。

1
Abhik