[2013-07-12 追加] 公式プラグインディレクトリに「WP Over Network」をリリースしました。管理画面からインストールできます。とりあえずの日本語の紹介はコチラのページになります。
[2013-06-07 追記] GitHub にプラグイン化したものをアップしています。良かったらご利用ください。フォーク歓迎。https://github.com/yuka2py/wp_over_network
WordPress をネットワーク化してマルチサイトを運用する際、ネットワーク上の各ブログの記事の更新情報をホストサイトのホームページなどに一覧表示させるという要件があると思います。
@HissyNC さんの「WordPressマルチサイトネットワークから新着記事を取得するコード(修正版)」を参考にして考えてみましたが、今回はネットワークサイトの更新情報をまとめたアーカイブページを持つ必要があったため、かわりに以下のような方法を行ってみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
<?php /** * 投稿日順で、ネットワーク上の全てのブログの投稿を取得する。 * @param mixed $args * numberposts 取得する投稿数。デフォルトは 5 * offset 取得する投稿のオフセット。デフォルトは false で指定無し。指定すると、paged より優先。 * paged 取得する投稿のページ数。get_query_var( 'paged' ) の値または1のいずれか大きな方。 * @return array<stdClass> */ function get_posts_over_network( $args=null ) { global $wpdb; $args = wp_parse_args( $args, array( 'numberposts' => 5, 'offset' => false, 'paged' => max( 1, get_query_var( 'paged' ) ), ) ); extract( $args ); //ページ指定とオフセットの調整 if ( $offset === false ) { $offset = ( $paged - 1 ) * $numberposts; } //《1》ネットワーク上のブログの一覧を取得 $query = sprintf( 'SELECT * FROM %sblogs', $wpdb->prefix ); $blogs = $wpdb->get_results( $query ); //《2》各ブログの投稿データを取得するサブクエリの準備 $sub_queries = array(); foreach ( $blogs as $blog ) { $blog_prefix = ( $blog->blog_id == 1 ) ? '' : $blog->blog_id . '_'; $sub_queries[] = implode(' ', array( sprintf( 'SELECT %3$d as blog_id, %1$s%2$sposts.* FROM %1$s%2$sposts', $wpdb->prefix, $blog_prefix, $blog->blog_id ), $wpdb->prepare('WHERE post_type = %s AND post_status = %s', 'post', 'publish'), )); } //《3》クエリの組み立て $query = array(); $query[] = 'SELECT SQL_CALC_FOUND_ROWS *'; $query[] = sprintf( 'FROM (%s) as posts', implode( ' UNION ALL ', $sub_queries ) ); $query[] = 'ORDER BY post_date DESC'; $query[] = sprintf( 'LIMIT %d, %d', $offset, $numberposts ); $query = implode( ' ', $query ); //《4》問い合わせの実行 $posts = $wpdb->get_results( $query ); $foundRows = $wpdb->get_results( 'SELECT FOUND_ROWS() as count' ); $foundRows = $foundRows[0]->count; //《5》wp_query の書き換え global $wp_query; $wp_query->query_vars['posts_per_page'] = $numberposts; $wp_query->found_posts = $foundRows; $wp_query->max_num_pages = ceil( $foundRows / $numberposts ); return $posts; } |
以下に簡単に解説します。
- 《1》で wp_blogs テーブルよりブログの一覧を取得
- 《2》で《1》で取得したブログから投稿を取得するサブクエリを準備
- 《3》でクエリ全体を組み立て
- 《4》で記事データと、総件数を取得
- 《5》で wp_query の変数の一部を書き換え
大きなポイントは、《3》で各サイトの投稿テーブルを UNION ALL してしまうことと、《5》で wp_query を書き換えることです。《5》を行う事によって、wp_pagenavi などのプラグインでページナビゲーションの表示が可能になります。なかなか泥臭いことをやっていますね。 (; ^ω^)
上記の関数は、例えば次のようにして利用できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
<?php get_header(); the_post(); ?> <section id="content-primary"> <header id="page-header"> <h1><?php the_title() ?></h1> </header> <?php $posts = get_posts_over_network(); wp_pagenavi(); ?> <?php if ( ! empty ($posts) ) : ?> <section class="post-list"> <?php foreach ( $posts as $post ) : switch_to_blog( $post->blog_id ); setup_postdata( $post ) ?> <section id="post-<?php the_ID() ?>" <?php post_class() ?>> <h2>【<?php echo get_bloginfo( $post->blog_id ) ?>】</h2> <h1><a href="<?php the_permalink() ?>"><?php the_title() ?></a></h1> <?php echo get_the_excerpt() ?> </section> <?php restore_current_blog(); endforeach; ?> </section> <?php else : ?> <p>投稿がありません。</p> <?php endif; # End of empty( $posts ) ?> </section> <?php wp_reset_query(); get_sidebar(); get_footer(); |
このサンプルは固定ページを準備して、固定ページのテンプレートに直接上記のコードを書いて、ネットワークブログのアーカイブページとして表示させるイメージです。
- 15行目で投稿データを取得
- 17行目で、wp_pagenavi を呼び出してページャーを表示しています。
- 24行目でブログを切り替え、25行目で投稿データをセットアップ
- 33行目でブログをカレントブログに戻します。
これで wp_pagenavi によるページナビゲーションもちゃんと表示されます。もちろんページの移動も可能です。
§
以下は、汎用的に使えるように幾つかオプションを足して、整理したものです。ご参考まで。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
<?php class network { /** * ネットワーク上の投稿データを取得します。 * @param mixed $args * numberposts 取得する投稿数。デフォルトは 5 * offset 取得する投稿のオフセット。デフォルトは false で指定無し。指定すると、paged より優先。 * paged 取得する投稿のページ数。get_query_var( 'paged' ) の値または1のいずれか大きな方。 * post_type 取得する投稿タイプ。デフォルトは post * orderby 並び替え対象。デフォルトは post_date * order 並び替え順。デフォルトは DESC で降順 * post_status 投稿のステータス。デフォルトは publish * blog_ids 取得するブログのIDを指定。デフォルトは null で指定無し * exclude_blog_ids 除外するブログのIDを指定。デフォルトは null で指定無し * affect_wp_query wp_query を書き換えるか否か。デフォルトは false で書き換えない。wp_pagenavi など wp_query を参照するページャープラグインの利用時には true とする * @return array<stdClass> */ function get_posts( $args=null ) { global $wpdb; $args = wp_parse_args( $args, array( 'numberposts' => 5, 'offset' => false, 'paged' => max( 1, get_query_var( 'paged' ) ), 'post_type' => 'post', 'orderby' => 'post_date', 'order' => 'DESC', 'post_status' => 'publish', 'blog_ids' => null, 'exclude_blog_ids' => null, 'affect_wp_query' => false, ) ); extract( $args ); //ページ指定とオフセットの調整 if ( $offset === false ) { $offset = ( $paged - 1 ) * $numberposts; } //ブログの一覧を取得 $blogs = self::get_blogs( compact( 'blog_ids', 'exclude_blog_ids' ) ); //投稿データを取得するサブクエリの準備 $sub_queries = array(); foreach ( $blogs as $blog ) { $blog_prefix = ( $blog->blog_id == 1 ) ? '' : $blog->blog_id . '_'; $sub_queries[] = implode(' ', array( sprintf( 'SELECT %3$d as blog_id, %1$s%2$sposts.* FROM %1$s%2$sposts', $wpdb->prefix, $blog_prefix, $blog->blog_id ), $wpdb->prepare('WHERE post_type = %s AND post_status = %s', $post_type, $post_status), )); } //クエリの組み立て $query[] = 'SELECT SQL_CALC_FOUND_ROWS *'; $query[] = sprintf( 'FROM (%s) as posts', implode( ' UNION ALL ', $sub_queries ) ); $query[] = sprintf( 'ORDER BY %s %s', $orderby, $order ); $query[] = sprintf( 'LIMIT %d, %d', $offset, $numberposts ); $query = implode( ' ', $query ); //問い合わせの実行 global $wpdb; $posts = $wpdb->get_results( $query ); $foundRows = $wpdb->get_results( 'SELECT FOUND_ROWS() as count' ); $foundRows = $foundRows[0]->count; //wp_query の書き換え if ( $affect_wp_query ) { global $wp_query; $wp_query->query_vars['posts_per_page'] = $numberposts; $wp_query->found_posts = $foundRows; $wp_query->max_num_pages = ceil( $foundRows / $numberposts ); } return $posts; } /** * ブログの一覧を取得する。 * 返される各ブログの情報を持つオブジェクトは、ブログ名とその Home URL を含む。 * @param mixed $args * blog_ids 取得するブログのIDを指定。デフォルトは null で指定無し * exclude_blog_ids 除外するブログのIDを指定。デフォルトは null で指定無し * @return array<stdClass> */ function get_blogs( $args=null ) { global $wpdb; $args = wp_parse_args( $args, array( 'blog_ids' => null, 'exclude_blog_ids' => null, ) ); extract( $args ); //必要に応じて、where 句を準備 $where = array(); if ( $blog_ids ) { if ( is_array( $blog_ids ) ) { $blog_ids = array_map( 'intval', (array) $blog_ids ); $blog_ids = implode( ',', $blog_ids ); } $where[] = sprintf( 'blog_id IN (%s)', $blog_ids ); } if ( $exclude_blog_ids ) { if ( is_array( $exclude_blog_ids ) ) { $exclude_blog_ids = array_map( 'intval', (array) $exclude_blog_ids ); $exclude_blog_ids = implode( ',', $exclude_blog_ids ); } $where[] = sprintf( 'blog_id NOT IN (%s)', $exclude_blog_ids ); } //クエリの組み立て $query[] = sprintf( 'SELECT * FROM %sblogs', $wpdb->prefix ); if ( $where ) { $query[] = "WHERE " . implode(' AND ', $where); } $query[] = 'ORDER BY blog_id'; $query = implode( ' ', $query ); //問い合わせの実行 $blogs = $wpdb->get_results( $query ); //各ブログの情報を取得 foreach ( $blogs as &$blog ) { switch_to_blog( $blog->blog_id ); $blog->name = get_bloginfo('name'); $blog->home_url = get_home_url(); restore_current_blog(); } return $blogs; } /** * 投稿データをブログとともにセットアップする。 * 内部的に switch_to_blog を使っているので、呼び出した後の処理が終わったら、 * restore_current_blog() を都度コールする * @param array $post 投稿データ。$post->blog_id を保持していること。 * @return void */ function setup_postdata_and_switch_to_blog( $post ) { switch_to_blog( $post->blog_id ); $post->blog_name = get_bloginfo( 'name' ); $post->blog_home_url = get_home_url(); setup_postdata( $post ); } } |
上記は、最初のサンプルとほぼ同様に、次のようにして利用できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
<?php get_header(); the_post(); ?> <section id="content-primary"> <header id="page-header"> <h1><?php the_title() ?></h1> </header> <?php //ホストブログを除いた一覧の取得。wp_pagenavi を利用するため、affect_wp_query=true とする $posts = network::get_posts('exclude_blog_ids=1&affect_wp_query=true'); wp_pagenavi(); ?> <?php if ( ! empty ($posts) ) : ?> <section class="post-list"> <?php foreach ( $posts as $post ) : network::setup_postdata_and_switch_to_blog( $post ); ?> <section id="post-<?php the_ID() ?>" <?php post_class() ?>> <h2>【<?php echo $post->blog_name ?>】</h2> <h1><a href="<?php the_permalink() ?>"><?php the_title() ?></a></h1> <?php echo get_the_excerpt() ?> </section> <?php restore_current_blog(); endforeach; ?> </section> <?php else : ?> <p>投稿がありません。</p> <?php endif; # End of empty( $posts ) ?> </section> <?php wp_reset_query(); get_sidebar(); get_footer(); |
以上です。
[…] WordPress のマルチサイトで、ネットワークブログの投稿を一覧表示する […]
まさに欲しいコードだったので助かります !!!!
1点だけ伺いたいのですが 、カテゴリーを指定することは可能でしょうか?
( 特定categoryページに入れたのですが、全カテゴリの記事が表示されてしまうため)
お忙しいかとは思いますが、ご返信お待ちしております。
ozomaruさん、こんばんわ。
プラグインの方では対応していませんが、上のコードであれば好きにアレンジされたら対応できるでしょう。SQLとWPのデータ構造が分かっていたら簡単な筈ですが、そのあたりは大丈夫ですか?
(以下、そうでない場合…)
おっしゃるカテゴリについて、もっと詳しい要件が無いとお答えしにくいです…。
例えばおっしゃるカテゴリはホストサイトだけの話なのか、あるいはネットワーク全体に渡っての話なのか? ネットワーク全体であれば、そのカテゴリは特定の1つなのか? 複数なのか? またカテゴリスラッグ、そしてそのIDは同じか? それから、post_type は post についての話だけなのか? あるいは他の post_type も絡んでくるのか? など、色々と考えられます。
情報があったら、何かアドバイスできるかも知れません。
yuka2pyさん
早速のご返信ありがとうございます。
SQLとWPのデータ構造までわかっていないため、曖昧な質問で申し訳ありませんでした。
ネットワーク全体・カテゴリは特定の1つ・カテゴリスラッグ&IDは同じ・post_type は post
になります。
自由自在にデータを操作できるレベルになりたいものです ; ;
宜しくお願いいたします!
お返事が遅くなってすみません。
こちらはプラグインの方で対応する予定としました。
https://github.com/yuka2py/wp-over-network/issues/2
実装的には少し面倒そうに思っています。
なるべく効率の良い(恥ずかしくない (^_^;A)実装にしたいなとは思っています。
[…] WordPress のマルチサイトで、ネットワークブログの投稿を一覧表示する https://foreignkey.toyao.net/archives/785 […]
はじめまして。
マルチサイトでの記事取得を検索してこちらの記事を見つけました。
投稿は表示できたのですが、カスタム投稿の表示は可能でしょうか?
post_typeに投稿とカスタム投稿を指定したのですが、表示することが出来ず、
カスタム投稿のみを指定した場合も表示できませんでした。
データベースなどについてあまり詳しくないのですが、教えていただくことは可能でしょうか?
よろしくお願いします。