web-dev-qa-db-ja.com

カスタム投稿タイプアーカイブとしてのページテンプレート

私の目標は、スラッグwww.domain.com/resourcesをArchive PageではなくPage Templateで使用し、そのスラッグの子として単一のCPT投稿を作成することです。 www.domain.com/resources/post

私はうまくいくだろうと思った解決策:

Wordpressで新しいページを作成し、それにカスタムページテンプレートを付けました。以下のように私のカスタム投稿タイプも登録しています。 has_archive および rewrite に特に注意してください。

function bb_resources() {
    register_post_type( 'resources', array(
            'labels' => array(
                    'name' => 'Resources',
                    'singular_name' => 'Resource',
            ),
            'taxonomies' => array('resources_cat'),
            'public' => true,
            'has_archive' => false,
            'show_ui' => true,
            'supports' => array( 'title', 'editor' ),
            'rewrite' => array('slug'=>'resources', 'with_front'=>false),
    ) );
}

ただし、これは単一の投稿ページに404を作成します。

私は困惑している、アドバイスしてください!

6
wackerfuss

WordPressバージョン4.4以降、フック 'theme_page_templates' で任意のページテンプレートを設定することができます。

つまり、ページ編集画面の[Page Template]メニューに任意の値を表示することができ、選択するとその値がそのページのページテンプレートメタに保存されます。

これは、あなたが「自動的に」登録されたCPTのいずれかのためのページテンプレートを作成できることを意味します。

設定ページのテンプレート

コードは次のようになります。

add_action( 'current_screen', function( \WP_Screen $screen ) {
    if( 'page' !== $screen->id || 'post' !== $screen->base ) {
        return;
    }

    // retrieve CPT objects with archive
    $types = get_post_types( ['has_archive' => true], 'objects' );

    // store CPT slug and labels in an array
    $menu = array();
    foreach( $types as $cpt ) {
        $menu[ $cpt->name ] = 'Archive: ' . $cpt->label;
    }

    // merge with page templates
    $menu and add_filter(
        'theme_page_templates',
        function( array $templates ) use ( $menu ) {
            $templates = array_merge( $templates, $menu );
            return $templates;
        }
    );
} );

このコードを配置した状態で、ページを作成または編集するときにhas_archiveresourcesに設定して投稿タイプtrueを登録したと仮定すると、[ページテンプレート]ドロップダウンにArchive: Resourcesという名前のページテンプレートが表示されます。

あなたはあなたがあなたのresources投稿を表示するのに使いたい、あなたがページを作成するときそのテンプレートを選ぶことができます。

直接ページアクセスをリダイレクトする

このページは1ページ以上のアーカイブなので、ページにアクセスしたときにユーザーを実際のCPTのアーカイブにリダイレクトするコードを使用できます。

add_action( 'template_redirect', function() {
    if( is_page_template() ) {

        // current page templates
        $template = get_page_template_slug( get_queried_object_id() );

        // array of CPT names, filtering the ones with archives
        $types = get_post_types( array( 'has_archive' => true ), 'names' );

        // if the current template is one of the CPT, redirect
        if( in_array( $template, $types, true ) ) {
            wp_safe_redirect( get_post_type_archive_link( $template ) );
            exit();
        }
    }
} );

ページデータを取得する

記事を閲覧するときには、おそらくそのページのコンテンツとタイトルを使用したいでしょう。

それでは、特定の投稿タイプのページを取得できるようにする関数を書きましょう。

function get_archive_page( $post_type = '' ) {

    // if no CPT given, let's use the current main query to get it
    if( ! $post_type ) {
        global $wp_query;
        $query_var = ( array ) $wp_query->get( 'post_type' );
        $post_type = reset($query_var);
    }

    // if we have a valid post type
    if( post_type_exists( $post_type ) ) {

        // let's query the first page that has a page template
        // named after the the post type
        $posts = get_posts( array(
            'post_type'      => 'page',
            'post_status'    => 'publish',
            'posts_per_page' => 1,
            'orderby'        => 'menu_order',
            'order'          => 'ASC',
            'meta_query' => array( array( 
                'key'   => '_wp_page_template',
                'value' => $post_type
            ) )
        ) );

        // if we have results, return first (and only) post object
        if( $posts ) {
            return reset($posts);
        }
    }
}

上記の関数を使って、ページのタイトルと内容を取得するための2つの追加関数を書くことができます。

function get_archive_page_title( $post_type = '' ) {
    $page = get_archive_page( $post_type );

    return ( $page ) ? get_the_title( $page ) : '';
}    

function get_archive_page_content( $post_type = '' ) {
    $page       = get_archive_page( $post_type );
    $content    = '';

    if( $page ) {
        setup_postdata( $page );
        ob_start();
            the_content();
        $content = ob_get_clean();
        wp_reset_postdata();
    }

    return $content;
}

function the_archive_page_title() {
    echo get_archive_page_title();
}

function the_archive_page_content() {
    echo get_archive_page_content();
}

関数名はそれらが何をするのかを明確にするはずです。

カスタムフィールドが必要な場合は、get_archive_page_meta()のような関数を書くこともできます。

アーカイブテンプレート内

これで、archive.php(またはarchive-resources.php)テンプレートで、上記の関数を使用してアーカイブページのタイトルと内容を表示できます。

それは次のようになります。

<h1><?php the_archive_page_title(); ?></h1>

<div><?php the_archive_page_content(); ?></div>

<?php
    while( have_posts() ) : the_post();
        // loop stuff here
    endwhile;
?>

これにより、CPTアーカイブのURLはexample.com/resources/になり、単一のCPTのURLはexample.com/resources/post-name/になります。

同時に、投稿タイプに固有のコンテンツとタイトル(および必要に応じてカスタムフィールド)を書くことができます。

また、このメソッドはどの投稿タイプにも再利用可能であると考えてください。

10
gmazzap

gmazzap答え はすごい!私は4.1以降、そのユースケースにぴったり合う2つの新しい関数、 the_archive_titlethe_archive_description を追加したいと思います。すでにそれらを使用しているテーマ(二十五十五のような)のためにあなたがテーマテンプレートを編集する代わりにこれをする最後の部分をスキップすることができます:

フィルタを介してデータを追加する

add_filter( 'get_the_archive_title', function( $title ) {
    if( is_post_type_archive() ) {   
        $page  = get_archive_page( $post_type );    // Function Found in @gmazzap's answer
        $title = $page ? get_the_title( $page ) : $title;
    }

    return $title;
} );

add_filter( 'get_the_archive_description', function( $content ) {
    if( is_post_type_archive() ) {
        $page    = get_archive_page( $post_type );  // Function Found in @gmazzap's answer
        $content = '';

        if( $page ) {
            setup_postdata( $page );
            ob_start();
                the_content();
            $content = ob_get_clean();
            wp_reset_postdata();
        }

        $content = ( $content !== '' ) ? get_the_title( $page ) : $content;
    }

    return $content;
} );
2
kraftner

これに対する私自身の方法は、前の2つの答えを読んだ後は明らかに穴が開いているように感じますが、私はとにかくそれを共有します。また、コンテンツ作成者にとって、ページベースの/ CMSスタイルのサイト構造で、特定のアーカイブの「概要」コンテンツをどこで編集するかを知るのは簡単ではありません。

regionと呼ばれるpost_typeについては、regionsと呼ばれる実際のWPページと共にそれを登録します。

page-regions.phpというテンプレート(またはそのページをそのページに接続するために使用する任意の戦略)を作成し、カスタムループを実行してすべての領域post_typeをプルします。テンプレートにテンプレート部分を使用する場合は、ページヘッダーと本文も含めるのが簡単で、コードはDRYのままです。 always という1つのpost_typeがあるため、カスタマイズも非常に簡単です。 :)

これにより、URLは わずかに になりますが、要求したものとは異なります。

http://example.com/regions アーカイブの投稿があるページの
http://example.com/region/africa または
http://example.com/region/asia 単一のCPTビューの場合。

しかし私にとっては、/region/africaは実際には/regions/africaよりも理にかなっていますが、それは標準であるRails/cakephpサイトで作業することによって推進されるかもしれません。 /regionだけで、(あなたがpost_typeをどのように設定したかによって)post_type = regionアーカイブになることになりますが、私は単にそれにリンクしません。 URLバーのサーファーがそこに着いても大したことはない。

やや副次的なボーナス:/regions-of-the-worldのように、オーバービュー/アーカイブページのURLを好きなように作ることができます。

0
Will