• WordPressでACF Proなしで画像ギャラリー(繰り返しフィールド)を自作する方法【コピペOK】

    WordPress

    WordPressでACF Proなしで画像ギャラリー(繰り返しフィールド)を自作する方法【コピペOK】

    Listen & Subscribe

    WordPressで施工事例やポートフォリオを作成する際、複数の画像を登録・並び替えできる「繰り返しフィールド」が欲しくなることはありませんか?

    Advanced Custom Fields(ACF)Proを使えば簡単に実装できますが、年間ライセンス費用がかかるのがネックです。

    しかし、実は、画像ギャラリーの繰り返しフィールドはWordPressの標準機能だけで自作できます。

    この記事では、ACF Proを使わずにカスタムメタボックスで画像ギャラリーを実装する方法を、実際のコード付きで解説します。

    この記事でできること

    • カスタム投稿タイプに画像ギャラリー機能を追加
    • WordPress標準のメディアライブラリから複数画像を選択
    • ドラッグ&ドロップで画像の並び替え
    • 個別の画像削除
    • ACF Proのライセンス費用を節約

    スポンサーリンク

    完成イメージ

    管理画面の投稿編集ページに、以下の機能を持つメタボックスが追加されます。

    • 「画像を追加」ボタンでメディアライブラリが開く
    • サムネイル一覧が表示され、ドラッグで並び替え可能
    • 各画像の右上に削除ボタン(×)を配置

    WordPressでACF Proなしで画像ギャラリー(繰り返しフィールド)を自作する方法【コピペOK】

    前提条件

    • カスタム投稿タイプ works(施工事例)を今記事の例とする
    • カスタム投稿タイプ works(施工事例)が登録済みであること
    • WordPress 5.0以上であること

    カスタム投稿タイプの登録がまだの場合は、functions.phpregister_post_type()works を登録してください。

    実装の全体像

    実装は大きく4つのパートに分かれます。

    1. 管理画面用スクリプトの読み込み — メディアライブラリとSortableを有効化
    2. メタボックスの追加 — 投稿編集画面にギャラリーUIを表示
    3. メタボックスのHTML/JS — 画像選択・削除・並び替えのUI実装
    4. 保存処理 — 画像IDの配列をポストメタに保存

    コード解説

    以下のコードを functions.php に追加してください。

    1. 管理画面用スクリプトの読み込み

    function works_gallery_admin_scripts($hook) {
     global $post_type;
     if (($hook === 'post.php' || $hook === 'post-new.php') && $post_type === 'works') {
      wp_enqueue_media();
      wp_enqueue_script('jquery-ui-sortable');
     }
    }
    add_action('admin_enqueue_scripts', 'works_gallery_admin_scripts');

    wp_enqueue_media() でWordPress標準のメディアアップローダーを、jquery-ui-sortable でドラッグ&ドロップの並び替え機能を読み込みます。

    $hook$post_type で条件を絞ることで、施工事例の編集画面でのみスクリプトをロードし、他の管理画面への影響を防いでいます。

    2. メタボックスの登録

    function works_gallery_meta_box() {
     add_meta_box(
      'works_gallery', // メタボックスのID
      '施工事例 画像ギャラリー', // 表示タイトル
      'works_gallery_meta_box_callback', // コールバック関数
      'works', // 対象の投稿タイプ
      'normal', // 表示位置
      'high' // 優先度
     );
    }
    add_action('add_meta_boxes', 'works_gallery_meta_box');

    add_meta_box() で投稿編集画面にカスタムメタボックスを追加します。

    第5引数の 'normal' はエディタの下に表示する指定、'high' は他のメタボックスより上に表示する優先度の指定です。

    3. メタボックスのHTML出力とJavaScript

    ここがメインの実装部分です。

    少し長いですが、やっていることはシンプルです。

    // メタボックスのHTML出力
    function works_gallery_meta_box_callback($post) {
      wp_nonce_field('works_gallery_nonce_action', 'works_gallery_nonce');
      $images = get_post_meta($post->ID, '_works_gallery_images', true);
      if (!is_array($images)) $images = array();
      ?>
      <style>
       #works-gallery-container {
         display: flex;
         flex-wrap: wrap;
         gap: 5px;
       }
       .works-gallery-item {
         cursor: grab;
         position: relative;
         display: inline-block;
       }
       .works-gallery-item:active {
         cursor: grabbing;
       }
       .works-gallery-item.ui-sortable-helper {
         opacity: 0.8;
         box-shadow: 0 4px 12px rgba(0,0,0,0.3);
         transform: scale(1.05);
       }
       .sortable-placeholder {
         width: 150px;
         height: 150px;
         border: 2px dashed #0073aa;
         background: #f0f6fc;
         display: inline-block;
         margin: 5px;
       }
     </style>
     <div id="works-gallery-container">
      <?php foreach ($images as $index => $image_id) :
       $image_url = wp_get_attachment_image_url($image_id, 'thumbnail');
       if (!$image_url) continue;
      ?>
      <div class="works-gallery-item">
        <img src="<?php echo esc_url($image_url); ?>" style="width:150px; height:150px; object-fit:cover; display:block;">
        <input type="hidden" name="works_gallery_images[]" value="<?php echo esc_attr($image_id); ?>">
        <button type="button" class="works-gallery-remove" style="position:absolute; top:0; right:0; background:red; color:#fff; border:none; cursor:pointer; padding:2px 6px;">×</button>
      </div>
      <?php endforeach; ?>
     </div>
     <p>
       <button type="button" id="works-gallery-add" class="button">画像を追加</button>
     </p>
     <p class="description">※ 画像をドラッグ&ドロップで並び替えできます</p>
     <script>
      jQuery(document).ready(function($) {
        $('#works-gallery-add').on('click', function(e) {
          e.preventDefault();
          var frame = wp.media({
            title: '画像を選択',
            multiple: true,
            library: { type: 'image' },
            button: { text: '画像を追加' }
          });
          frame.on('select', function() {
            var attachments = frame.state().get('selection').toJSON();
            $.each(attachments, function(i, attachment) {
              var thumbUrl = attachment.sizes.thumbnail ? attachment.sizes.thumbnail.url : attachment.url;
              var html = '<div class="works-gallery-item">';
              html += '<img src="' + thumbUrl + '" style="width:150px; height:150px; object-fit:cover; display:block;">';
              html += '<input type="hidden" name="works_gallery_images[]" value="' + attachment.id + '">';
              html += '<button type="button" class="works-gallery-remove" style="position:absolute; top:0; right:0; background:red; color:#fff; border:none; cursor:pointer; padding:2px 6px;">×</button>';
              html += '</div>';
              $('#works-gallery-container').append(html);
            });
          });
          frame.open();
        });
        $(document).on('click', '.works-gallery-remove', function() {
        $(this).closest('.works-gallery-item').remove();
      });
      $('#works-gallery-container').sortable({
        items: '.works-gallery-item',
        cursor: 'grabbing',
        placeholder: 'sortable-placeholder',
        tolerance: 'pointer',
        revert: 200
      });
    });
    </script>
    <?php 
    }

    ポイント解説

    nonceフィールドwp_nonce_field() でCSRF対策を実施しています。保存時にこのnonceを検証することで、不正なリクエストを防ぎます。

    hidden input:各画像の attachment IDworks_gallery_images[] という配列名のhiddenフィールドで保持します。Sortableで並び替えるとDOM順が変わるため、フォーム送信時に自動的に正しい順序で送信されます。

    wp.media:WordPressに組み込まれているメディアライブラリのモーダルを利用しています。multiple: true で複数選択に対応しています。

    jQuery UI Sortable.sortable() を呼ぶだけでドラッグ&ドロップの並び替えが実現します。placeholder オプションで、ドロップ先に青い点線の枠を表示しています。

    4. 保存処理

    function works_gallery_save($post_id) {
     // nonceの検証
     if (!isset($_POST['works_gallery_nonce']) ||
     !wp_verify_nonce($_POST['works_gallery_nonce'], 'works_gallery_nonce_action')) return;
    
     // 自動保存時はスキップ
     if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
    
     // 権限チェック
     if (!current_user_can('edit_post', $post_id)) return;
    
     // 画像IDの保存
     if (isset($_POST['works_gallery_images'])) {
      $images = array_map('intval', $_POST['works_gallery_images']);
      update_post_meta($post_id, '_works_gallery_images', $images);
     } else {
      delete_post_meta($post_id, '_works_gallery_images');
     }
    }
    add_action('save_post_works', 'works_gallery_save');

    保存処理では3つのセキュリティチェックを行っています。

    • nonce検証:リクエストが正規のフォームからであることを確認
    • 自動保存の除外:WordPressの自動保存でメタデータが消えるのを防止
    • 権限チェック:投稿を編集する権限を持つユーザーかを確認

    array_map('intval', ...) で送信された値をすべて整数に変換し、不正な値の混入を防いでいます。

    save_post_works フックは save_post_{post_type} 形式で、施工事例(works)の保存時にのみ発火します。

    フロントエンドでの表示方法

    保存した画像をフロントエンドで表示するには、テンプレートファイルに以下のコードを追加します。

    <?php
     $images = get_post_meta(get_the_ID(), '_works_gallery_images', true);
     if (!empty($images) && is_array($images)) : ?>
     <div class="works-gallery">
     <?php foreach ($images as $image_id) :
     $image_url = wp_get_attachment_image_url($image_id, 'large');
     $image_alt = get_post_meta($image_id, '_wp_attachment_image_alt', true);
     if (!$image_url) continue;
     ?>
      <figure class="works-gallery__item">
       <img src="<?php echo esc_url($image_url); ?>"
        alt="<?php echo esc_attr($image_alt); ?>"
        loading="lazy">
      </figure>
    <?php endforeach; ?>
    </div>
    <?php endif; ?>

    wp_get_attachment_image_url() の第2引数でサイズを指定できます。

    一覧ページでは medium、詳細ページでは largefull を使い分けるとよいでしょう。

    まとめ

    ACF Proの繰り返しフィールドは便利ですが、画像ギャラリー程度の機能であればWordPressの標準機能だけで十分に実装できます。

    今回紹介したコードは約100行程度で、メディアライブラリの活用、ドラッグ&ドロップの並び替え、セキュリティ対策まで含んだ実用的な実装です。プラグインへの依存を減らしたい方や、WordPressの内部構造を学びたい方はぜひ試してみてください。