投稿で使用された画像を本文下に一覧表示する方法

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

はじめに

投稿で本文に使用された画像を一覧表示したい場合、単純にget_attached_media()を使うだけでは不十分です。
なぜなら、この関数は投稿に直接アップロードされた画像しか取得できず、再利用した画像や他の投稿からの画像は取得できないからです。

そこで今回は、Gutenbergエディターの画像ブロック解析に加え、クラシックエディター(HTML本文)の<img>タグ解析にも対応させた、本文・アイキャッチ・過去の再利用画像まで完全に網羅して一覧表示します。

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

get_attached_media()による画像取得の課題

get_attached_media()を使うと、投稿内で使用されている画像を全て取得できるわけではありません(表1参照)。

get_attached_media()の動作
画像の状況get_attached_media() で取得可能か?備考
投稿で直接アップロードした画像可能post_parentが投稿に紐付いている
投稿Bで投稿Aの画像を再利用不可能post_parentは投稿Aのまま
投稿のアイキャッチ画像可能アイキャッチも投稿に紐付く場合は取得される
ギャラリー内にある画像不可能ギャラリーはブロック内の参照であり post_parent に依存しない
外部アップロードした画像(URL指定など)不可能投稿に物理的に紐付いていない

完成イメージ

実装手順

以下の手順で実装します。

  • ラベル
    PHPを追加

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

    /**
     * ブロック配列を再帰的に走査して画像ブロックのIDを収集
     *
     * @param array $blocks ブロック構造の配列
     * @param array $image_ids 画像IDを保持する参照配列
     * @return void
     */
    if (!function_exists('collect_block_image_ids')):
    function collect_block_image_ids(array $blocks, array &$image_ids) {
      foreach ($blocks as $block) {
        if (
          isset($block['blockName']) &&
          $block['blockName'] === 'core/image' &&
          !empty($block['attrs']['id'])
        ) {
          $image_id = (int) $block['attrs']['id'];
    
          if ($image_id > 0) {
            $image_ids[$image_id] = true;
          }
        }
    
        // インナーブロックを再帰的に走査
        if (!empty($block['innerBlocks']) && is_array($block['innerBlocks'])) {
          collect_block_image_ids($block['innerBlocks'], $image_ids);
        }
      }
    }
    endif;
    
    
    /**
     * 本文HTMLから画像IDを収集
     *
     * @param string $content 本文HTML
     * @param array $image_ids 画像IDを保持する参照配列
     * @return void
     */
    if (!function_exists('collect_content_image_ids')):
    function collect_content_image_ids(string $content, array &$image_ids) {
      if (!preg_match_all('/<img\b[^>]*\bsrc=["\']([^"\']+)["\'][^>]*>/i', $content, $matches)) {
        return;
      }
    
      foreach ($matches[1] as $image_url) {
        $image_id = attachment_url_to_postid($image_url);
    
        if ($image_id > 0) {
          $image_ids[$image_id] = true;
        }
      }
    }
    endif;
    
    
    // 本文を変更
    add_filter('the_content', function ($content) {
      if (!is_singular('post')) {
        return $content;
      }
    
      $post_id   = get_the_ID();
      $eye_catch = true;
      $image_ids = [];
    
      // アイキャッチ画像のIDを取得
      if ($eye_catch && has_post_thumbnail($post_id)) {
        $thumb_id = (int) get_post_thumbnail_id($post_id);
    
        if ($thumb_id > 0) {
          $image_ids[$thumb_id] = true;
        }
      }
    
      // 本文ブロックを解析して画像ブロックのIDを収集
      $blocks = parse_blocks(get_post_field('post_content', $post_id));
      collect_block_image_ids($blocks, $image_ids);
    
      // クラシック本文から画像IDを収集
      collect_content_image_ids(get_post_field('post_content', $post_id), $image_ids);
    
      if (empty($image_ids)) {
        return $content;
      }
    
      // 画像のズーム表示設定を取得
      $zoom = get_image_zoom_effect();
    
      ob_start();
      ?>
      <div class="used-images-gallery">
        <h2 class="used-images-gallery__title">この記事で使用されている画像</h2>
        <div class="used-images-gallery__grid">
          <?php foreach (array_keys($image_ids) as $id): ?>
            <?php
            $full = wp_get_attachment_url($id);
            $img  = wp_get_attachment_image($id, 'medium', false, ['class' => 'used-images-gallery__image']);
    
            if (!$full || !$img) {
              continue;
            }
            ?>
    
            <figure class="used-images-gallery__item">
              <?php if ($zoom !== 'none') : ?>
                <a href="<?= esc_url($full); ?>" class="used-images-gallery__link">
                  <?= $img; ?>
                </a>
              <?php else : ?>
                <?= $img; ?>
              <?php endif; ?>
            </figure>
          <?php endforeach; ?>
        </div>
      </div>
      <?php
      return $content . ob_get_clean();
    });
  • ラベル
    CSSを追加

    以下のコードをstyle.cssに追加します。

    :root {
      --my-column: 4;
    }
    
    /* 表示幅≦834pxのときは2列に変更 */
    @media (width <= 834px) {
      :root {
        --my-column: 2;
      }
    }
    
    .used-images-gallery__grid {
      display: grid;
      gap: 15px;
      grid-template-columns: repeat(var(--my-column), minmax(0, 1fr));
    }
    
    .used-images-gallery__item img {
      aspect-ratio: 1 / 1;
      object-fit: cover;
    }

WordPress 7.1の「添付画像を使用」機能との違い

「ギャラリー」ブロック

WordPress 7.1で追加されたギャラリーの自動追加機能は、該当の投稿に「直接アップロード(添付)されたメディア」(データベース上の post_parent)を判定対象とします(図2参照)。

そのため、本文中に貼り付けていない画像も拾える反面、過去にアップロードした画像を本文で再利用(参照)した場合はギャラリーに含まれません(表1参照)。

一方、本コードは「記事の本文ブロック(parse_blocks)内に実際に設置されている画像」を直接抽出します。アップロード経路に関わらず、記事内で実際に使われている画像だけを正確にギャラリー表示できます。

さいごに

今回紹介した方法を使えば、投稿本文で使用された画像を漏れなく抽出し、本文下に一覧表示することができます。
アイキャッチ画像を含めるかどうかも簡単に切り替えられ、読者に記事内で使用されている画像を視覚的に一覧で示すことができ、記事の利便性や見栄えが向上します。

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