-
WordPressでACF Proなしで画像ギャラリー(繰り返しフィールド)を自作する方法【コピペOK】
Listen & Subscribe
WordPressで施工事例やポートフォリオを作成する際、複数の画像を登録・並び替えできる「繰り返しフィールド」が欲しくなることはありませんか?
Advanced Custom Fields(ACF)Proを使えば簡単に実装できますが、年間ライセンス費用がかかるのがネックです。
しかし、実は、画像ギャラリーの繰り返しフィールドはWordPressの標準機能だけで自作できます。
この記事では、ACF Proを使わずにカスタムメタボックスで画像ギャラリーを実装する方法を、実際のコード付きで解説します。
目次
この記事でできること
- カスタム投稿タイプに画像ギャラリー機能を追加
- WordPress標準のメディアライブラリから複数画像を選択
- ドラッグ&ドロップで画像の並び替え
- 個別の画像削除
- ACF Proのライセンス費用を節約
スポンサーリンク
完成イメージ
管理画面の投稿編集ページに、以下の機能を持つメタボックスが追加されます。
- 「画像を追加」ボタンでメディアライブラリが開く
- サムネイル一覧が表示され、ドラッグで並び替え可能
- 各画像の右上に削除ボタン(×)を配置

前提条件
- カスタム投稿タイプ
works(施工事例)を今記事の例とする - カスタム投稿タイプ
works(施工事例)が登録済みであること - WordPress 5.0以上であること
カスタム投稿タイプの登録がまだの場合は、
functions.phpにregister_post_type()でworksを登録してください。実装の全体像
実装は大きく4つのパートに分かれます。
- 管理画面用スクリプトの読み込み — メディアライブラリとSortableを有効化
- メタボックスの追加 — 投稿編集画面にギャラリーUIを表示
- メタボックスのHTML/JS — 画像選択・削除・並び替えのUI実装
- 保存処理 — 画像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 IDをworks_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、詳細ページではlargeやfullを使い分けるとよいでしょう。まとめ
ACF Proの繰り返しフィールドは便利ですが、画像ギャラリー程度の機能であればWordPressの標準機能だけで十分に実装できます。
今回紹介したコードは約100行程度で、メディアライブラリの活用、ドラッグ&ドロップの並び替え、セキュリティ対策まで含んだ実用的な実装です。プラグインへの依存を減らしたい方や、WordPressの内部構造を学びたい方はぜひ試してみてください。
