かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

Wordpress 記事を属性 順序( menu_order ) の順番に並べたい

追記: 2015-09-15
この方法では、順序( menu_order )に同じ値を指定していると、前後の投稿リンクが上手く動作しません。
新しく修正版を書きましたので、こちらを参照ください。


Wordpressの固定ページには属性の欄に順序があり表示する順番を決めることができます。
この順序ですが投稿やカスタム投稿でもこの属性欄を表示させることがで、順序に入力した順番で表示させることもできます。

 

管理画面 投稿に属性(順序)の項目を表示する

add_post_type_support関数を使って属性(順序のみ)を表示させることができます。
関数リファレンス/add post type support - WordPress Codex 日本語版
function.phpに下記コードを記入します。

<?php /* function.php */
add_post_type_support( 'post', 'page-attributes' );

 

管理画面 カスタム投稿タイプに属性(順序)の項目を表示する

先のadd_post_type_supportの第一引数が投稿タイプなので'post'の部分を作成しているカスタム投稿タイプ名に変更するだけでもOKですが、カスタム投稿タイプを設定する際のsupportsに'page-attributes'を指定する事で実現できます。
関数リファレンス/register post type - WordPress Codex 日本語版

<?php /* function.php */
add_action( 'init', 'codex_book_init' );
/**
 * カスタム投稿タイプ book を登録する。
 *
 * @link http://codex.wordpress.org/Function_Reference/register_post_type
 */
function codex_book_init() {
  $labels = array(
    'name'               => _x( 'Books', 'post type general name', 'your-plugin-textdomain' ),
    'singular_name'      => _x( 'Book', 'post type singular name', 'your-plugin-textdomain' ),
    'menu_name'          => _x( 'Books', 'admin menu', 'your-plugin-textdomain' ),
    'name_admin_bar'     => _x( 'Book', 'add new on admin bar', 'your-plugin-textdomain' ),
    'add_new'            => _x( 'Add New', 'book', 'your-plugin-textdomain' ),
    'add_new_item'       => __( 'Add New Book', 'your-plugin-textdomain' ),
    'new_item'           => __( 'New Book', 'your-plugin-textdomain' ),
    'edit_item'          => __( 'Edit Book', 'your-plugin-textdomain' ),
    'view_item'          => __( 'View Book', 'your-plugin-textdomain' ),
    'all_items'          => __( 'All Books', 'your-plugin-textdomain' ),
    'search_items'       => __( 'Search Books', 'your-plugin-textdomain' ),
    'parent_item_colon'  => __( 'Parent Books:', 'your-plugin-textdomain' ),
    'not_found'          => __( 'No books found.', 'your-plugin-textdomain' ),
    'not_found_in_trash' => __( 'No books found in Trash.', 'your-plugin-textdomain' )
  );

  $args = array(
    'labels'             => $labels,
    'public'             => true,
    'publicly_queryable' => true,
    'show_ui'            => true,
    'show_in_menu'       => true,
    'query_var'          => true,
    'rewrite'            => array( 'slug' => 'book' ),
    'capability_type'    => 'post',
    'has_archive'        => true,
    'hierarchical'       => false,
    'menu_position'      => null,
    'supports'           => array(
      'title',
      'editor',
      'author',
      'thumbnail',
      'excerpt',
      'comments',
      'page-attributes', // 属性(順序)を表示する
    )
  );

  register_post_type( 'book', $args );
}

 

管理画面の一覧を順序 で並べる

順序で順番を指定できますが、管理画面の投稿一覧ページは順序属性の設定や入力に関係なく公開日順になってしまいます。
orderbyをmenu_orderにする事で順序の設定順になるようなのでpre_get_postsを使ってorderbyを設定してやります。

<?php /* function.php */
function my_posts_per_page( $wp_query ) {
  // 管理画面の時
  if( is_admin() ) {
    // アーカイブページ かつ page-attributes をサポートしている時
    if( $wp_query->is_post_type_archive()
    && post_type_supports( $wp_query->query_vars['post_type'], 'page-attributes' )) {
      if( !isset( $wp_query->query_vars['orderby'] ) ) {
        // orderby が明示的に指定されていなければ、順序(menu_order)で並び替える
        $wp_query->query_vars['orderby'] = 'menu_order';
      }
      if ( !isset( $wp_query->query_vars['order'] ) ) {
        // order が明示的に指定されていなければ、順序(menu_order)の数字が小さい方から並べる為にASCを指定
        $wp_query->query_vars['order'] = 'ASC';
      }
    }
  }
}
add_action( 'pre_get_posts', 'my_posts_per_page' );

カスタム投稿タイプの時に限定したい時はis_post_type_archive()関数の第一引数に投稿タイプを指定すればOKです。
関数リファレンス/is post type archive - WordPress Codex 日本語版
上のpost_type="book"の例だと下記のようになります。

<?php /* function.php */
function my_posts_per_page( $wp_query ) {
  if( is_admin() ) {
    // カスタム投稿タイプ book の時のみ
    if( $wp_query->is_post_type_archive('book')
    && post_type_supports( $wp_query->query_vars['post_type'], 'page-attributes' )) {
      if( !isset( $wp_query->query_vars['orderby'] ) ) {
        $wp_query->query_vars['orderby'] = 'menu_order';
      }
      if ( !isset( $wp_query->query_vars['order'] ) ) {
        $wp_query->query_vars['order'] = 'ASC';
      }
    }
  }
}
add_action( 'pre_get_posts', 'my_posts_per_page' );

 

表示画面もアーカイブページは順序(menu_order) で並べる

先の記述で管理画面の一覧を順序(menu_order) 順に並べることは出来ましたが、
表側の画面でも同様にorderbyをmenu_orderに指定しないと順序(menu_order)の指定で表示されません。
管理画面でも表側でも同様にmenu_order順で表示させたい場合は先程のコードの管理画面に限定していた所を外せばOKです。
function.phpを編集します。

<?php /* function.php */
function my_posts_per_page( $wp_query ) {
  // アーカイブページ かつ page-attributes をサポートしている時
  if( $wp_query->is_post_type_archive()
  && post_type_supports( $wp_query->query_vars['post_type'], 'page-attributes' )) {
    if( !isset( $wp_query->query_vars['orderby'] ) ) {
      // orderby が明示的に指定されていなければ、順序(menu_order)で並び替える
      $wp_query->query_vars['orderby'] = 'menu_order';
    }
    if ( !isset( $wp_query->query_vars['order'] ) ) {
      // order が明示的に指定されていなければ、順序(menu_order)の数字が小さい方から並べる為にASCを指定
      $wp_query->query_vars['order'] = 'ASC';
    }
  }
}
add_action( 'pre_get_posts', 'my_posts_per_page' );


これで、表画面、管理画面共にアーカイブページは順序(menu_order)の順番に表示されるようになりした。
しかし、投稿ページ(single.php)などで、前後の投稿へのリンクは順序(menu_order)で指定した通りにはなっていません。

 

前後の投稿へのリンク(previous_post_link, next_post_link)を順序(menu_order)で指定したもので取得させる

注意!この方法では、順序( menu_order )に同じ値を指定していると、前後の投稿リンクが上手く動作しません。
修正版の記事を参照ください Wordpress 記事を属性 順序( menu_order ) の順番に並べたい 同じ順序があっても大丈夫版 - かもメモ


前後の投稿へのリンクを表示させるprevious_post_link, next_post_linkは内部的にget_adjacent_post()関数を呼んでおり、 get_adjacent_postに設定されているfilterを利用することで前後の投稿を順序(menu_order)で指定したもので取得することができます。

当初get_{$adjacent}_post_sortフィルターでクエリーのORDERBYだけ書き換えれば上手くいくかと思ったのですがget_{$adjacent}_post_whereのWHERE文も変更してやる必要がありました。
function.phpを編集してfilterを書き加えます。

<?php /* function.php */
// previous
function my_previous_post_where($where, $in_same_term, $excluded_terms) {
  global $post, $wpdb;
  $post_type = get_post_type($post);
  if($post_type == 'post') {
    return $wpdb->prepare( "WHERE p.menu_order < %s AND p.post_type = %s AND p.post_status = 'publish' $posts_in_ex_terms_sql", $post->menu_order, $post->post_type);
  }
  // 対象でない場合もreturnが必要
  return $where;
}
// next
function my_next_post_where($where, $in_same_term, $excluded_terms) {
  global $post, $wpdb;
  $post_type = get_post_type($post);
  if($post_type == 'post') {
    return $wpdb->prepare( "WHERE p.menu_order > %s AND p.post_type = %s AND p.post_status = 'publish' $posts_in_ex_terms_sql", $post->menu_order, $post->post_type);
  }
  // 対象でない場合もreturnが必要
  return $where;
}
add_filter('get_previous_post_where', 'my_previous_post_where', 10, 3);
add_filter('get_next_post_where', 'my_next_post_where', 10, 3);

// previous
function my_previous_post_sort($sort) {
  global $post;
  $post_type = get_post_type($post);
  if($post_type == 'post') {
    return 'ORDER BY p.menu_order DESC LIMIT 1';
  }
  // 対象でない場合もreturnが必要
  return $sort;
}
// next
function my_next_post_sort($sort) {
  global $post;
  $post_type = get_post_type($post);
  if($post_type == 'post') {
    return 'ORDER BY p.menu_order ASC LIMIT 1';
  }
  // 対象でない場合もreturnが必要
  return $sort;
}
add_filter('get_previous_post_sort', 'my_previous_post_sort');
add_filter('get_next_post_sort', 'my_next_post_sort');

カスタム投稿タイプのみに適応したい場合は$post_type == 'post'の部分をカスタム投稿タイプに変更して下さい。
また、カスタム投稿タイプ複数使用している場合全てのfilterで対象のpost_typeでない場合もreturn $where;return $sort;の様に元の値を返してあげないと前後のリンクに通常の投稿(post)が混ざったりと意図しない挙動になってしまうので注意が必要です。



これで、一覧画面も各投稿画面でも順序(menu_order)で指定した順に投稿を表示させることができました。



[参考にしました]