【WordPress】複数サイトの新着記事を「更新順」にまとめる(サイトごとの表示から卒業する)

このサイトの記事の下に「デザインの記事」っていう欄があるんですけどー
知ってますー、他のサイトの新着記事が出てくるやつですよねー
designnokoto.com と itukanokoto.com の記事が交互に出てほしかったんですが、よく見ると新しい順になってなくないですかー?
……気づいちゃいました? 実はそうなんです。今回はそれを直した話です。

何が起きていたか

以前の記事で紹介した実装は、こんな感じでサイトのURLを1つずつ処理していました。

$rss = $feed->loadRss('https://designnokoto.com/feed');
foreach ( $rss->item as $item ) {
  // 1件表示
}

$rss = $feed->loadRss('https://itukanokoto.com/feed');
foreach ( $rss->item as $item ) {
  // 1件表示
}

これ、動くんです。動くから気づかないんです。

でも「更新順」にはなっていません。designnokoto の記事を先に出し切ってから、itukanokoto の記事を出す、というコードに書いた順番で出ているだけです。

サイトごとに表示 ≠ 更新順に表示

たとえば

  • designnokoto の最新記事: 9/1
  • itukanokoto の最新記事: 9/13

だったら、9/13の方を先に見せたいですよね。でも「サイトごとに1件ずつ」というロジックだと、どちらが本当に新しいかは一切見ずに、コードの並び順のまま出てしまいます。

サイトが2つのときは気づきにくいんですが、3つ・4つと増えてくると「あのサイト、全然更新されてないのに毎回一番上に出る」みたいな不自然さが目立ってきます。

直し方(配列に集めてからソートする)

やることは実はシンプルです。表示する前に、全サイトの記事を1つの配列に集めて、日付でソートしてから、上位n件だけ表示する。

function get_combined_feed_items( $total_limit = 4 ) {

    $sources = array(
        array( 'url' => 'https://designnokoto.com/feed', 'limit' => 3 ),
        array( 'url' => 'https://itukanokoto.com/feed',  'limit' => 3 ),
    );

    $all = array();

    foreach ( $sources as $source ) {
        $rss = get_feed_channel( $source['url'] ); // 取得は今までと同じでOK
        $i = 0;
        foreach ( $rss->item as $item ) {
            if ( $i >= $source['limit'] ) {
                break;
            }
            $all[] = array(
                'title'     => (string) $item->title,
                'link'      => (string) $item->link,
                'timestamp' => strtotime( (string) $item->pubDate ),
                // ...他に使う項目も同じ形で入れる
            );
            $i++;
        }
    }

    // ここがポイント。全部集めたあとに、日付の新しい順で並べ替える
    usort( $all, function ( $a, $b ) {
        return $b['timestamp'] <=> $a['timestamp'];
    } );

    return array_slice( $all, 0, $total_limit );
}

ポイントは2つだけです。

  • limit(サイトごとの候補数) と total_limit(最終的に表示する件数) を分けること。1サイトだけ更新が多いときに、そのサイトが表示枠を独占しないようにするためです。
  • usort() で timestamp を比較して並べ替える。<=>(PHP7以降の比較演算子)を使うと1行で書けます。

呼び出し側(表示するテンプレート)は、こう1回呼ぶだけになります。

foreach ( get_combined_feed_items( 4 ) as $item ) {
    // カードを1枚出す
}

サイトごとの二重ループが、1つのループにまとまりました。

新しいサイトを増やすときも1行で済む

この形にしておくと、紹介したいサイトが増えたときも $sources の配列に1行追加するだけで済みます。

array( 'url' => 'https://apartment-morning.com/feed', 'limit' => 3 ),

並べ替えのロジックは共通なので、サイトが3つでも5つでも同じコードで「更新順」になります。

さいごに

「サイトごとに表示」と「更新順に表示」、動きだけ見るとどちらも記事カードがちゃんと並ぶので、バグに気づきにくいタイプの不具合でした。

複数サイトの情報を1箇所にまとめるときは、「表示する前に1つの配列に集めて並べ替える」を思い出してもらえたら、今回の記事は成功です。次回は、このデザインの記事欄の横に置いている「サイト紹介バナー」の作り方について書く予定です。

整理整頓、得意ですかー?
コードは得意ですー、机の上は苦手ですー

デザコトauthor

あ、いいな、と思うWebデザインを紹介しています。デザインの参考に。やさしいデザインが多いです。Webデザインギャラリー『デザインのこと - Web design gallery』を運営しています。