set a CPT as parent of a page

时间:2015-09-30 作者:Cyclonecode

我有一个自定义的帖子类型,叫做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,
      )
   );
}
我希望能够将事件设置为普通页面的父级。换句话说,我希望我的所有活动都能在Parent 选择下的元素Page Attributes.

我读过很多关于如何将CPT设置为页面子级的帖子,但不是相反。

2 个回复
最合适的回答,由SO网友:birgire 整理而成

我们在这里所面临的挑战是如何:

不要删除或替换当前的页面属性元框

在下拉列表中保留层次结构信息。

我们假设我们在单页编辑屏幕上,并希望添加event 将类型发布到page 父级下拉列表

使用显示下拉列表wp_dropdown_pages() 函数,调用get_pages() 作用它只支持单个post类型。

有两种方法可以做到这一点:

方法#1-修改SQL这是一种实验方法,因为它处理SQL修改

因为没有明显的方法来过滤生成的SQL查询get_pages(), 我们可以使用generalquery 根据我们的需要进行筛选。

为了使下拉列表更加用户友好,当它包含来自多个帖子类型的标题时,我们使用list_pages 过滤器,用于前置帖子类型信息。

Example:

因此,不要显示以下选项:

"(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"

Demo plugin:

以下是可以放在插件中进行测试的代码片段:

/**
 * 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() 运行两次,然后将其合并到一个下拉列表中;一次用于page 发布类型和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;
}
在这里,两个层次下拉列表由下面的空选择事件选项分隔开来。

/**
 * 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 );
 调整主查询假设现在我们创建了一个名为怪物和男人信息的页面omam-info 使用omam 鼻涕虫

那么路径就是

example.tld/omam/omam-info
但这会产生404错误。原因是get_page_path() 检查内部\\WP_Query 类的主查询失败:

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_idqueried_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 我们在上面使用的过滤器。

SO网友:Abhik

首先,你需要移除核心Page Attributes Meta框,然后添加您自己的,除了更改父帖子类型外,保持所有内容不变。这是我的代码,经过调整后可用于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>\';
    }
}  
EDIT:刚才注意到,页面属性元框中有一个过滤器可用于修改默认参数:

$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 );  
所以,可能有一种非常简单的方法来做你想做的事。

相关推荐

rewrite rules hierarchical

我希望我的WordPress遵循以下规则:/productes/ 包含所有产品的页面/productes/[category]/ 包含该类别所有产品的分类页面/productes/[category]/[subcategory]/ 包含该类别所有产品(与2相同)的分类页面/[productes]/[category]/[product]/ A.single-productes.php 将显示类别产品/[productes]/[category]/[subcategory]/[product]/ A.sin