REST APIを使って複数のサイトから取得した新着情報を混合してカード表示させるコード例

公開日:2024(令和6)年9月25日/最終更新日:

About WP REST API | Personal WP Customization Notes (PWCN)

【景品表示法に基づく表記】ページ内のコンテンツには、商品プロモーションが含まれています



WordPressの機能として結構重宝するREST API。今回はそれを使って以下のことをショートコードで出力するという機能を実装してみました。

  1. 複数のサイトから〇件の新着情報を取得する
  2. 1を混ぜて、公開日または更新日で並び替えた上で指定した件数だけ表示させる
    • カード形式で表示させる
    • 最終的に表示される件数を指定できるようにする
    • 抜粋を表示させるかどうかを判断させる
    • オフセット値を設定して、最終結果から〇件以降の情報を表示できるようにする
    • データは指定した秒数キャッシュさせて表示速度に影響が出にくくする
    • 日付はサイトの日付フォーマットに従った形で表示させる

実は当初、REST APIを使って複数のサイトから取得した新着投稿を混ぜて並び替えて表示させてみよう!で始めたものの、後からいろいろとしたいことが増えてきたためコードがかなり長くなってしまいました..。

まあこれだけやれることが満載されていれば、あとはショートコード内のパラメーターで調整するとか、不要なコード部分は削除すればいいのかな?という感じです。

本ページで掲載しているコードは、以下に了承した上で使用ください

  • コードは商用・非商用問わず自由に使っていただいて構いませんが、コード追加による不具合やトラブルが発生しても当方では一切責任を負いません
  • コードは有効化しているテーマのfunctions.php、style.cssなどへ追加することで機能します。それらのファイルへの変更を行うことに不安のある方は使用しないでください
  • コードは本ページの公開日時点で私の環境において動作したものです。WordPressバージョン他環境の違いによって動作しないことがあります
  • コードは、セキュリティ、コードの正確さなどにおいて完全なものではありません。中には紹介するコードを簡略化するために省略している部分があるものもありますので、ご自身でコードを十分に検証し、必要な部分の編集を行った上で使用するようにしてください
  • 掲載しているのは参考コードです。自身の環境に合わせるための編集はご自身で対応いただく必要があります(コメント欄等から質問いただいても基本回答は致しません)
  • 掲載しているコードの転載を禁じます(SNSで紹介いただいたり、本ページへのリンクを張っていただくことは大歓迎です)

表示サンプル

こんな感じで表示できます(以下のサンプルではこのサイトの新着情報になっていますが、URLを指定すれば他サイトの情報も表示できます)

複数サイトの新着情報をREST APIを使って取得して混在表示させるコード

以下がコードの全体像です。自身の環境に合わせて変更した上で有効化しているテーマのfunctions.phpへ追加してください。

ショートコードプログラム部分

このようなカスタマイズをされる方は、恐らくコードの流れなどは理解されているかと思いますので解説は割愛します。各所に何をしているかをコメントアウトしていますので、自身でよく見て解釈してください。

/*** REST APIを使って複数のサイトから新着情報を取得して並び替えて表示させる ***/
/* cache_time 0で再取得 */
/* order_by dateなら公開日順 */
function pwcn_rest_api_latest_posts_with_thumbnails( $atts ) {
//パラメーターの指定
	$atts = shortcode_atts( array(
		'sites' => '', // サイトのURLをカンマ区切りで指定
		'number' => 5, // 取得する投稿数
		'display_count' => 5, // 表示する投稿数
		'order_by' => 'modified', // 並び順(modifiedまたはdate)
		'cache_time' => 3600, // キャッシュの有効期限(秒)
		'datetext' => '1',//日付の接頭辞
		'des' => '',
		'offset' => 0, // オフセット
	), $atts );

//パラメーターが空またはショートコード内にない場合の初期値設定(エラー回避)
	if( empty( $atts['sites'] ) ){ $atts['sites'] = get_home_url(); }//空の場合は自サイトのホームURLをセット
	if( empty( $atts['number'] ) ){ $atts['number'] = '5';}
	if( empty( $atts['display_count'] ) ){ $atts['display_count'] = '5';}
	if( empty( $atts['order_by'] ) ){ $atts['order_by'] = 'modified';}
	if( empty( $atts['cache_time'] ) ){ $atts['cache_time'] = 3600;}
	if( empty( $atts['datetext'] ) ){ $atts['datetext'] = '1';}
	if( empty( $atts['des'] ) ){ $atts['des'] = '';}
	if( empty( $atts['offset'] ) ){ $atts['offset'] = 0;}

//キャッシュキーを生成
	$cache_key = 'pwcn_rest_latest_posts_' . md5( $atts['sites']) . $atts['number'] . $atts['display_count'] . $atts['order_by'] . $atts['offset'] ;

//cache_timeが0の場合、キャッシュを削除して再取得
	if ( intval( $atts['cache_time'] ) === 0 ) {
		delete_transient( $cache_key );
	}

// キャッシュを取得
	$cached_posts = get_transient( $cache_key );

	if ( false !== $cached_posts ) {
		$posts = $cached_posts;
	} else {
		$sites = explode( ',', $atts['sites'] );
		$posts = [];

	foreach ( $sites as $site ) {
	// REST APIから新着投稿を取得
		$response = wp_remote_get( esc_url( $site . '/wp-json/wp/v2/posts?per_page=' . $atts['number'] ) );

		if ( is_array( $response ) && ! is_wp_error( $response ) ) {
			$body = json_decode( wp_remote_retrieve_body( $response ), true );
				if ( is_array( $body ) ) {
					foreach ( $body as $post ) {
					// アイキャッチ画像のIDを取得
						$thumbnail_id = $post['featured_media'];
						$thumbnail = '';

					// アイキャッチ画像のURLを取得
						if ( $thumbnail_id ) {
							$media_response = wp_remote_get( esc_url( $site . '/wp-json/wp/v2/media/' . $thumbnail_id ) );
							if ( is_array( $media_response ) && ! is_wp_error( $media_response ) ) {
								$media_body = json_decode( wp_remote_retrieve_body( $media_response ), true );
								$medium_size_info = $media_body['media_details']['sizes']['medium'];

								//"medium"サイズのURLと幅・高さを取得
									$medium_size_url = $medium_size_info['source_url'];
									$medium_size_width = $medium_size_info['width'];
									$medium_size_height = $medium_size_info['height'];
							}
						}

					// 投稿データを追加
						$posts[] = array(
							'title' => $post['title']['rendered'],
							'description' => $post['excerpt']['rendered'],
							'link' => $post['link'],
							'date' => $post['date_gmt'], // 公開日を追加
							'modified' => $post['modified_gmt'], // 更新日を追加
							'thumbnail' => $medium_size_url,
							'width' => $medium_size_width,
							'height' => $medium_size_height,
						);
					}
				}
		}
	}

		// 並び替えの基準を決定
			usort( $posts, function( $a, $b ) use ( $atts ) {
				$key = $atts['order_by'] === 'date' ? 'date' : 'modified';
				return strtotime( $b[$key] ) - strtotime( $a[$key] );
			});

		// 表示する投稿数で制限
			$posts = array_slice( $posts, intval($atts['offset']), intval($atts['display_count']));

		// キャッシュに保存
			set_transient( $cache_key, $posts, intval( $atts['cache_time'] )); // cache_timeを整数に変換
	}

// 投稿を表示
	$output = '<ul class="pwcn-mapi-card-wrapper">';

	foreach ( $posts as $post ) {//リストの繰り返しここから
		//サイトの日付フォーマットを格納
			$format = get_option( 'date_format' );

		//アイキャッチ画像があるかによって$image_htmlへ格納
			if ( $post['thumbnail'] ) {
				$img_html = '<a href="' . esc_url( $post['link'] ) . '"><img src="' . esc_url( $post['thumbnail'] ) . '" alt="' . esc_attr( $post['title'] ) . '" width="'.esc_attr($post['width']).'" height="'.esc_attr($post['height']).'"></a>';
			} else {
				$nothumb_text = __('No Thumbnail','');
				$img_html = '<span>'.$nothumb_text.'</span>'; // アイキャッチ画像がない場合
			}
		
		//タイトルを$title_htmlへ格納
			$title_html = '<a href="' . esc_url( $post['link'] ) . '">' . esc_html( $post['title'] ) . '</a>';

		//抜粋を$description_htmlへ格納
			$description_html = $post['description'];
		//抜粋を表示するかどうかをパラメーターから判断して$descriへ格納
		if( $atts['des'] == '0' || empty($atts['des'])){
			$descri = '';
		}else if( $atts['des'] == '1' ){
			$descri = '<a href="' . esc_url( $post['link'] ) . '">' .$description_html . '</a>';
		}else{
			$descri = '';
		}

		//公開日・更新日を$moddate_html $date_htmlへ格納
			$moddate_html = '<a href="' . esc_url( $post['link'] ) . '">' . esc_html( date( $format, strtotime( $post['modified'] ) ) ) . '</a>';
			$date_html = '<a href="' . esc_url( $post['link'] ) . '">' .esc_html( date( $format, strtotime( $post['date'] ) ) ) . '</a>';
		
		//日付の接頭辞を設定
			//文字列
				$pub_text = __('Publish Date:','');
				$mod_text = __('Modified Date:','');

			//文字かアイコンかの分岐
			if($atts['datetext'] == '1'){
				$pub_tag = '<span class="icon-calendar" style="margin-right:4px;"></span>';
				$mod_tag = '<span class="icon-loop2" style="margin-right:4px;"></span>'; 
			}else{
				$pub_tag = '<span>'.$pub_text.'</span>';
				$mod_tag = '<span>'.$mod_text.'</span>';
			}

		//出力するhtmlをまとめる
		$output .= '<li>';
		$output .= '<div class="pwcn-mapi-card-inner">';
		$output .= '<div class="pwcn-mapi-card-img">';
		$output .= $img_html;
		$output .= '</div>';
		$output .= '<div class="pwcn-mapi-card-content">';
		$output .= '<div class="pwcn-mapi-card-title">';
		$output .= $title_html;
		$output .= '</div>';
		$output .= '<div class="pwcn-mapi-card-date">';
		$output .= $mod_tag. $moddate_html;
		$output .= ' / ';
		$output .= $pub_tag. $date_html;
		$output .= '</div>';
		$output .= '<div class="pwcn-mapi-card-description">';
		$output .= $descri;
		$output .= '</div>';
		$output .= '</div>';
		$output .= '</div>';
		$output .= '</li>';

	}//リストの繰り返しここまで

		$output .= '</ul>';

	return $output;
}
add_shortcode( 'pwcn_latest_posts_with_thumbnails', 'pwcn_rest_api_latest_posts_with_thumbnails' );

コードの追加がうまくいったら、以下のショートコードを投稿などへ挿入して試してみてください。

[pwcn_latest_posts_with_thumbnails sites="" number="" order_by="" display_count="" cache_time="" datetext="" des="" offset=""]

以下がショートコード内のパラメーター値と設定方法です。

パラメーター説明初期値
sitesサイトのURLを「,」区切りで入力します(末尾の/スラッシュは除きますなし
number各サイトから取得する投稿の件数を指定します5
order_by並び順の基準を「date(公開日)」にするか「modified(最終更新日)」にするかを指定しますmodified
display_count表示させる件数を指定します5
cache_timeデータをキャッシュする秒数を指定します
※0にするとキャッシュしないようになります
3600
datetext日付の接頭辞を文字列にするかアイコンにするかを指定します文字列
des抜粋を表示させるかを指定します
※1で非表示になります
なし(表示)
offset結果をオフセットする数を指定しますなし

スタイルの出力部分

スタイルコードはCSSへ書かず、インラインで出力するよう以下のようなプログラムで書いています。そのまま使う場合にはターゲットとなるCSSファイルのIDを書き換えるか、中のスタイルコード部分だけをテーマのスタイルシートへ貼り付けてください。

function pwcn_rest_api_latest_posts_with_thumbnails_style_init(){
if(wp_is_mobile()){
$custom_css_pre = hau_sanitize_css('
/* PWCN REST API Post List With Thumbnail For Mobile */
ul.pwcn-mapi-card-wrapper {
	list-style: none;
	padding-left: 0;
}

ul.pwcn-mapi-card-wrapper li {
	padding: 12px;
	border: 1px solid #eee;
	border-radius: 4px;
	margin-bottom:12px;
}

.pwcn-mapi-card-inner {
	width: 100%;
}

.pwcn-mapi-card-content {
	width: 100%;
}

.pwcn-mapi-card-img img {
	width: 100%;
	height: auto;
	object-fit: cover;
}

.pwcn-mapi-card-title {
	font-size: var(--wp--preset--font-size--large);
	font-weight:600;
}

.pwcn-mapi-card-date {
	font-size: var(--wp--preset--font-size--small);
	text-align:right;
}

.pwcn-mapi-card-title a,
.pwcn-mapi-card-description a,
.pwcn-mapi-card-date a
{
	text-decoration:none;
}
');
}else{
$custom_css_pre = hau_sanitize_css('
/* PWCN REST API Post List With Thumbnail For PC */
ul.pwcn-mapi-card-wrapper {
	list-style: none;
	padding-left: 0;
}

ul.pwcn-mapi-card-wrapper li {
	padding: 12px;
	border: 1px solid #eee;
	border-radius: 4px;
	margin-bottom:12px;
}

.pwcn-mapi-card-inner {
	display: flex;
	gap: 12px;
}

.pwcn-mapi-card-content {
	width: 100%;
}

.pwcn-mapi-card-img img {
	width: 150px;
	height: auto;
	max-height: 150px;
	object-fit: cover;
}

.pwcn-mapi-card-title {
	font-size: var(--wp--preset--font-size--large);
	font-weight:600;
}

.pwcn-mapi-card-date {
	font-size: var(--wp--preset--font-size--small);
	text-align:right;
}

.pwcn-mapi-card-title a,
.pwcn-mapi-card-description a,
.pwcn-mapi-card-date a
{
	text-decoration:none;
}

.pwcn-mapi-card-title a:hover,
.pwcn-mapi-card-description a:hover,
.pwcn-mapi-card-date a:hover
{
	text-decoration:underline;
}
');
}

//タブを除去
$custom_css_deltab = preg_replace('/\t+/', '', $custom_css_pre);
//改行を除去
$custom_css = str_replace(PHP_EOL, '', $custom_css_deltab);

//インラインで出力
	wp_add_inline_style( 'hatt3-style', $custom_css );
}
add_action('wp_enqueue_scripts','pwcn_rest_api_latest_posts_with_thumbnails_style_init');

スタイルコードは一例です。自身の環境に合わせて適宜変更してください

このコードには、基礎となる形態とともに、HTML出力時に余分な空白やタブを削除したり、モバイルとパソコンで条件分岐をする..といった要素も含んでいますので、このケース以外でもインラインでスタイルを出力する際の参考になると思います

後述

以前に、単独のサイトからREST API経由で情報を取得して一覧表示するという情報を公開しました。

そしたら、複数のサイトからREST APIで情報を取るにはどうしたらいいの?という疑問というか欲が湧いてきて、結局いろいろ試したけどよくわからなかったので、基礎部分は生成AIに手伝ってもらいました。

そして提案されたものを見て、複数のサイトからの情報を取って日付で並び替えてショートコードで出力するという基本部分が分かってから、以下の機能を自身で追加していきました。

  • ショートコード内にパラメーターがなかった場合にエラーを回避したい(初期値を設定したい)
  • 出力する件数(表示する件数)を指定したい
  • 抜粋を表示させたい(パラメーターで表示/非表示できるようにしたい)
  • 出力されるアイキャッチ画像のサイズを指定して、縦横サイズをタグ内に記述したい(CLS対策)
  • パラメーターを使って並び順を「公開日順」「最終更新日順」にできるようにしたい
  • パラメーターを使って並び替えた結果をオフセットさせたい
  • 毎回取得して表示させると表示速度が遅いので、キャッシュ(データベースに一時保存)させたい、キャッシュ時間をパラメーターでコントロールしたい
  • 日付表示の接頭辞を文字列ではなくアイコンにしたい
  • スタイルはカード形式にして、モバイルとパソコンそれぞれでいい感じになるようにしたい

ちなみに本当に初期に提示されたのは以下のコードです。

function fetch_latest_posts_with_thumbnails( $atts ) {
    $atts = shortcode_atts( array(
        'sites' => '', // サイトのURLをカンマ区切りで指定
        'number' => 5, // 取得する投稿数
    ), $atts );

    $sites = explode( ',', $atts['sites'] );
    $posts = [];

    foreach ( $sites as $site ) {
        // REST APIから新着投稿を取得
        $response = wp_remote_get( esc_url( $site . '/wp-json/wp/v2/posts?per_page=' . $atts['number'] ) );

        if ( is_array( $response ) && ! is_wp_error( $response ) ) {
            $body = json_decode( wp_remote_retrieve_body( $response ), true );
            if ( is_array( $body ) ) {
                foreach ( $body as $post ) {
                    // アイキャッチ画像のIDを取得
                    $thumbnail_id = $post['featured_media'];
                    $thumbnail = '';

                    // アイキャッチ画像のURLを取得
                    if ( $thumbnail_id ) {
                        $media_response = wp_remote_get( esc_url( $site . '/wp-json/wp/v2/media/' . $thumbnail_id ) );
                        if ( is_array( $media_response ) && ! is_wp_error( $media_response ) ) {
                            $media_body = json_decode( wp_remote_retrieve_body( $media_response ), true );
                            $thumbnail = !empty( $media_body['source_url'] ) ? $media_body['source_url'] : '';
                        }
                    }

                    // 投稿データを追加
                    $posts[] = array(
                        'title' => $post['title']['rendered'],
                        'link' => $post['link'],
                        'date' => $post['date'],
                        'thumbnail' => $thumbnail,
                    );
                }
            }
        }
    }

    // 公開日で並び替え
    usort( $posts, function( $a, $b ) {
        return strtotime( $b['date'] ) - strtotime( $a['date'] );
    });

    // 投稿を表示
    $output = '<ul>';
    foreach ( $posts as $post ) {
        $output .= '<li>';
        if ( $post['thumbnail'] ) {
            $output .= '<img src="' . esc_url( $post['thumbnail'] ) . '" alt="' . esc_attr( $post['title'] ) . '" style="max-width:100px; height:auto;">';
        } else {
            $output .= '<span>No Thumbnail</span>'; // アイキャッチ画像がない場合
        }
        $output .= '<a href="' . esc_url( $post['link'] ) . '">' . esc_html( $post['title'] ) . '</a> - ' . esc_html( date( 'Y/m/d', strtotime( $post['date'] ) ) ) . '</li>';
    }
    $output .= '</ul>';

    return $output;
}
add_shortcode( 'latest_posts_with_thumbnails', 'fetch_latest_posts_with_thumbnails' );

これと比べると、先に紹介した完成コードはかなり長いものになっていますが、時間があるようでしたらどのように変更を加えているのかを見てみると、より知識が深まるかも知れません。

ここから見えるREST APIの利便性と危険性

このページを見て、「へえ~REST APIって便利だな」と感じた方は多いかも知れません。

でもこれは、自身が運営しているサイト(単独または複数)で行う場合の利便性であって、これを他人が勝手に情報取得していったらどうでしょう?

WordPressは基本的に制限なくREST APIによる投稿データの出力を行う設定になっているので、何も知らずに運営していると、知らず知らずのうちに、他人のサイトで勝手に掲載されてしまうという危険性があるかも知れません(それでもよければ特に標準のままでいいのですが...)。

今回は投稿データを引っ張って表示させるというものですが、その情報の中には本文全体の文章も含まれるので、ひょっとするとパクリ被害に遭うかも知れませんし、REST APIはユーザー情報なども取得できるので、ユーザーIDなどが漏洩する危険性もあります。

つまり、REST APIは利便性と危険性が隣り合わせなのです。

危険性を回避するためにREST API経由の情報取得を制限しておきたいという方は以下のページを参考に対策をされることをおすすめします。

ちなみにこのサイトは上記ページの機能を実装して「管理画面の編集画面で必要な場合」と「特定のプラグインが利用するもの」、そして「同一サーバー、同一グローバルIPアドレスからの要求であること」という条件を満たさなければ情報が取得できないようにしており、本ページのコードなどを使って情報を引き出すことはできないようにしていますので、アクセスして情報が取得できないことを確認してみるといいと思います。

, , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,



Lolipop ServerMoshimo Ad x-serverMoshimo Ad

About WP REST API | Personal WP Customization Notes (PWCN)
プラグインの公式ページにある情報をWordPress.org APIで取得し、ショートコードで表示させる方法