以前クライアントから、こんな依頼を受けたことがあります。
「複数のWordPressブログの投稿をまとめて表示して、しかも「日付」「画像」「サイト名」「記事タイトル」をぜんぶ新着順に並べたい」というもの。
正直、これには少し手こずりました。
RSSフィードを使って他のWordPressサイトの記事を読み込むこと自体はやったことがあったのですが、これだけ多くの要素を一括で表示して、しかも整ったレイアウトにまとめるとなると、話は別だったんですね。
特に“サイト名”の取得で引っかかりました。日本語で調べてもなかなか情報が出てこず、海外の開発フォーラムを探し回ってようやく辿り着いた、というのが正直なところです。
そこで今回は、そのときに実装したPHPコードと、構築時に気をつけたいポイントを備忘録としてまとめておきます。なお、この記事のコードはWordPress標準の fetch_feed()(内部でSimplePieを使っています)を使う現行のやり方に書き直してあります。
複数ブログの日付・画像・サイト名・タイトルを取得する方法
必要な準備と前提
- WordPress環境でPHPが編集できること(テーマファイルやカスタムテンプレートなど)
- 読み込みたいブログがRSS2.0形式のフィードを出力していること
- WordPress標準の
fetch_feed()が使えること(内部でSimplePieが動きます)
実装サンプルコード(PHP)
以下は、複数のRSSフィードを読み込み、日付順に並べて「日付・画像・サイト名・記事タイトル」を表示するコードです。
昔のWordPressではMagpieRSS(wp-includes/rss.php や MAGPIE_* といった定数)を使う書き方が主流でしたが、これはかなり前に非推奨となり、いまはSimplePieベースの fetch_feed() に置き換わっています。fetch_feed() はURLの配列をそのまま渡すと、複数フィードをまとめて(SimplePieのマルチフィード機能で)1つに合体し、新着順に並べてくれるのがありがたいところです。
<?php
// 読み込みたいフィードのURLを配列で指定
$rss_arr = array(
'https://www.webernote.net/?feed=rss2',
'https://www.webernote2.net/?feed=rss2',
'https://www.webernote3.net/?feed=rss2',
);
// 配列を渡すと複数フィードがマージされ、日付の新しい順に並ぶ
$rss = fetch_feed( $rss_arr );
$rss_items = array();
if ( ! is_wp_error( $rss ) ) {
// 全フィードの中から最新5件を取得
$maxitems = $rss->get_item_quantity( 5 );
// 0番目から指定件数ぶんのアイテムを取り出す
$rss_items = $rss->get_items( 0, $maxitems );
}
?>
<ol class="clearfix">
<?php if ( empty( $rss_items ) ) : ?>
<li>No items.</li>
<?php else : ?>
<?php foreach ( $rss_items as $item ) : ?>
<li class="clearfix">
<?php
// 本文中から最初の画像を1枚取り出す
$first_img = '';
if ( preg_match( '/<img.+?src=[\'"]([^\'"]+?)[\'"].*?>/msi', $item->get_content(), $matches ) ) {
$first_img = $matches[1];
}
?>
<?php if ( ! empty( $first_img ) ) : ?>
<a href="<?php echo esc_url( $item->get_permalink() ); ?>" class="image" target="_blank" rel="noopener">
<img src="<?php echo esc_url( $first_img ); ?>" alt="" style="width:80px;height:60px;object-fit:cover;">
</a>
<?php else : ?>
<span class="image">
<img src="<?php echo esc_url( get_template_directory_uri() ); ?>/img/noimg.png" alt="no image">
</span>
<?php endif; ?>
<div class="info">
<p class="date"><?php echo esc_html( $item->get_date( 'Y.n.j' ) ); // 日付 ?></p>
<h4 class="title">
<a href="<?php echo esc_url( $item->get_permalink() ); ?>" target="_blank" rel="noopener">
[<?php echo esc_html( $item->get_feed()->get_title() ); // サイト名 ?>] <?php echo esc_html( $item->get_title() ); // 記事タイトル ?>
</a>
</h4>
</div>
</li>
<?php endforeach; ?>
<?php endif; ?>
</ol>
ポイントは、サイト名を $item->get_feed()->get_title() で取り出しているところです。ここがググっても全然出てこなくて、地味に苦労した部分でした(汗)。クラス名は私が実際に使ったコードのままなので、そちらのCSSに合わせて適宜変更してください。
ちなみに、複数フィードを配列で渡した場合は、SimplePie側が投稿日の新しい順に自動でソートしてくれます。なので「日付順に並べ替える」処理をこちらで書く必要はありません。件数を増やしたいときは get_item_quantity( 5 ) の数字を変えればOKです。
RSSフィードのキャッシュ時間を変える方法
WordPressが取得したRSSフィードは、一定時間キャッシュされます(初期値は12時間=43200秒)。そのため、元のブログを更新しても、こちら側の一覧にはすぐには反映されません。表示スピードやサーバー負荷を考えると、通常はキャッシュを効かせたままで問題ありませんが、動作確認をしているときはもう少し短くしたいことがあります。
キャッシュ時間を変えたいときは、wp_feed_cache_transient_lifetime フィルタを使います。fetch_feed() を呼ぶ直前にフィルタを追加し、呼んだあとに外す、という書き方がおすすめです。
function my_feed_cache_lifetime( $seconds ) {
return 600; // キャッシュを10分に短縮
}
add_filter( 'wp_feed_cache_transient_lifetime', 'my_feed_cache_lifetime' );
$rss = fetch_feed( $rss_arr );
remove_filter( 'wp_feed_cache_transient_lifetime', 'my_feed_cache_lifetime' );
テストの都度キャッシュを完全に無効化したいなら、wp_feed_options アクションでキャッシュ自体をオフにする手もあります。
function do_not_cache_feeds( $feed ) {
$feed->enable_cache( false );
}
add_action( 'wp_feed_options', 'do_not_cache_feeds' );
ただしキャッシュを切ると、ページを開くたびに毎回フィードを取りに行くので、表示が重くなったりサーバーに負荷がかかったりします。確認が終わったら元に戻しておくのが無難です。なお functions.php は、余計な文字が混じったり記述を間違えたりするとサイト全体が真っ白になることがあるので、編集は慎重に。
複数ブログの新着投稿を一括表示するということ
複数のWordPressサイトの新着情報を一画面にまとめて表示する——言葉にすると難しそうですが、RSSフィードを使えば思いのほかシンプルに実現できます。
しかも「日付」「画像」「サイト名」「記事タイトル」といった情報まで一度に引き出せるので、ニュースポータルや、企業グループ内メディアの統合など、いろいろな場面で応用がききます。
今回のコードは構成の自由度も高く、CSSを工夫すれば見た目もぐっと整えられます。必要に応じて、カテゴリやタグで絞り込んだり、表示件数を増減したり、Ajaxで読み込むように拡張したりもできます。
私の場合はサイト名の取得で回り道をしましたが、仕組みさえ分かってしまえば「やってみたら意外とできた」という類のものです。
つまづきやすいポイントを整理すると、次の3つです。
- 複数フィードは
fetch_feed()にURLの配列を渡すだけでマージされ、日付順に並ぶ - サイト名は
$item->get_feed()->get_title()で取得する - 反映が遅いと感じたら
wp_feed_cache_transient_lifetimeでキャッシュ時間を調整する
このあたりを押さえておけば、案件でも自社サイトでも同じやり方で対応できるはずです。