WordPress標準の「RSS」ウィジェットやブロックにサムネイル画像(og:image)を表示する方法

Cocoonカスタマイズ集
この記事は約9分で読めます。
記事内に広告が含まれています。

はじめに

以下を実現します。

WordPress標準の「RSS」ウィジェットやブロックは、標準機能のままでは記事のタイトルや概要、日付しか表示されず、サムネイル画像が表示されません。
テキストばかりのリストだと、どうしてもユーザーの目を引きにくくなってしまいます。

そこで、RSSで取得したリンク先のHTMLからog:imageを自動で取得し、「RSS」ウィジェットやブロックにサムネイル画像として追加します。

この記事では、この方法について説明します。

完成イメージ

実装手順

以下のコードをfunctions.phpに追加します。

/**
 * RSSの出力HTMLにog:imageを追加
 *
 * @param string $html 元のRSS出力HTML
 * @return string サムネイル構造に変換されたHTML
 */
if (!function_exists('convert_rss_html_to_thumb_layout')):
function convert_rss_html_to_thumb_layout($html) {
  $pattern = '/(<li[^>]*>)(.*?href=["\']([^"\']+)["\'].*?)(?=<\/li>)/is';

  return preg_replace_callback($pattern, function ($matches) {
    $li_start    = $matches[1];
    $all_content = $matches[2];
    $url         = $matches[3];

    $og_image = get_rss_og_image_url($url);

    $image_html = '';
    if ($og_image) {
      $image_html = '<img src="' . esc_url($og_image) . '" alt="" class="rss-widget-thumbnail">';
    }

    // 元のli開始タグを活かしつつ、内部をサムネイルとコンテンツに構造化
    return $li_start . '<div class="rss-item-thumb">' . $image_html . '</div><div class="rss-item-content">' . $all_content . '</div>';
  }, $html);
}
endif;


/**
 * 外部URLからog:imageを取得
 *
 * @param string $url 取得対象の外部サイトのURL
 * @return string 抽出されたog:imageのURL(空の場合は空文字)
 */
if (!function_exists('get_rss_og_image_url')):
function get_rss_og_image_url($url) {
  if (empty($url)) {
    return '';
  }

  // キャッシュデータを取得
  $cache_key = 'og_img_php_' . md5($url);
  $cached_img = get_transient($cache_key);

  if (false !== $cached_img) {
    return $cached_img;
  }

  // APIリクエスト
  $response = wp_remote_get($url, [
    'timeout' => 3,
  ]);

  if (is_wp_error($response)) {
    return '';
  }

  // 取得したレスポンスからHTMLを抽出
  $html = wp_remote_retrieve_body($response);
  if (empty($html)) {
    return '';
  }

  // og:imageを抽出
  $image_url = '';

  if (
    preg_match('/<meta\s+[^>]*property=["\']og:image["\'][^>]*content=["\']([^"\']+)["\']/i', $html, $matches) ||
    preg_match('/<meta\s+[^>]*content=["\']([^"\']+)["\'][^>]*property=["\']og:image["\']/i', $html, $matches)
  ) {
    $image_url = $matches[1];
  }

  // キャッシュデータを保存
  set_transient($cache_key, $image_url, 12 * HOUR_IN_SECONDS);

  return $image_url;
}
endif;


// フロントエンド表示に反映
add_filter('widget_display_callback', function ($instance, $widget, $args) {
  if (!isset($widget->id_base) || $widget->id_base !== 'rss') {
    return $instance;
  }

  ob_start();

  $widget->widget($args, $instance);
  $widget_output = ob_get_clean();

  echo convert_rss_html_to_thumb_layout($widget_output);

  return false;
}, 10, 3);


// 「RSS」ブロックに反映
add_filter('render_block_core/rss', function ($block_content, $block) {
  return convert_rss_html_to_thumb_layout($block_content);
}, 10, 2);


// フロント用JS・CSSを追加
add_action('wp_enqueue_scripts', function() {
  ob_start();
  ?>
  .wp-block-rss li,
  .widget_rss li {
    border-bottom: 1px dotted #ccc;
    max-width: 400px;
    padding: 10px 0;
  }

  .wp-block-rss li:first-child,
  .widget_rss li:first-child {
    padding-top: 0;
  }

  .wp-block-rss li:last-child,
  .widget_rss li:last-child {
    border-bottom: 0;
    padding-bottom: 0;
  }
  <?php

  $handle = 'rss-thumb-layout';

  wp_register_style($handle, false);
  wp_enqueue_style($handle);
  wp_add_inline_style($handle, ob_get_clean());
});

参考:Cocoonでブログカード化する方法

WordPress標準の「RSS」ウィジェットやブロックはテキストのみの表示で、サムネイルが表示されません。
そこで、Cocoonの場合、RSSの各項目をブログカード形式に置換して表示させます。

完成イメージ

実装手順

以下のコードをfunctions.phpに追加します。

/**
 * HTML文字列に含まれるul要素をブログカードに変換
 *
 * @param string $html 変換対象のHTML文字列
 * @return string 置換済みのHTML文字列
 */
if (!function_exists('convert_rss_html_to_thumb_layout')):
function convert_rss_html_to_thumb_layout($html) {
  $dom = new DOMDocument();
  libxml_use_internal_errors(true);
  $dom->loadHTML('<?xml encoding="UTF-8">' . $html);
  libxml_clear_errors();

  $xpath = new DOMXPath($dom);

  // HTML内の全てのulタグを抽出
  $uls = $xpath->query('//ul');
  foreach ($uls as $ul) {
    // ul内の各liに含まれるリンクURLを取得
    $links     = $xpath->query('.//li//a', $ul);
    $blogcards = '';

    foreach ($links as $link) {
      $url = $link->getAttribute('href');
      if ($url) {
        // ブログカードを出力
        $blogcards .= do_shortcode('[blogcard url="' . esc_url($url) . '"]');
      }
    }

    // ul要素をブログカード群に置き換える
    $fragment = $dom->createDocumentFragment();
    $fragment->appendXML($blogcards);
    $ul->parentNode->replaceChild($fragment, $ul);
  }

  // 変更したHTMLを返却
  $result = '';
  foreach ($dom->getElementsByTagName('body')->item(0)->childNodes as $node) {
    $result .= $dom->saveHTML($node);
  }

  return $result;
}
endif;


// フロントエンド表示に反映
add_filter('widget_display_callback', function ($instance, $widget, $args) {
  if (!isset($widget->id_base) || $widget->id_base !== 'rss') {
    return $instance;
  }

  ob_start();

  $widget->widget($args, $instance);
  $widget_output = ob_get_clean();

  echo convert_rss_html_to_thumb_layout($widget_output);

  return false;
}, 10, 3);


// 「RSS」ブロックに反映
add_filter('render_block_core/rss', function ($block_content, $block) {
  return convert_rss_html_to_thumb_layout($block_content);
}, 10, 2);

さいごに

今回紹介した方法を使えば、テキスト主体で地味になりがちだった「RSS」ウィジェットの表示を、一気に視覚的で魅力的なコンテンツへとアップグレードできます。
他サイトの更新情報をサムネイル画像付きで表示することで、ユーザーのクリック率向上も期待できるでしょう。

タイトルとURLをコピーしました