WordPressの未添付画像を自動で記事に紐付ける方法

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

はじめに

メディア→ライブラリをリスト形式にして「未添付」で絞り込むと、投稿に添付されていない画像の一覧が表示されます。

しかし、画像ごとに「投稿に添付」から個別に設定することは可能ですが、投稿一覧から該当の投稿を人手で選択しなければならず、手間がかかってしまいます。

そこで、一括操作に「自動で投稿に添付」という選択肢を追加し、チェックボックスで選択した画像を一括処理できるようにします。
新着順に投稿本文を自動でチェックし、画像ファイル名が含まれている最初に見つかった投稿へ自動的に割り当てます。

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

Gutenbergエディターで画像ブロックを配置しても「未添付」になる理由

Gutenbergエディターを採用している環境では、メディアライブラリから既にアップロード済みの画像を呼び出して記事内に「画像ブロック」として配置しても、データベース上では「投稿に添付(post_parent)された状態」にはならないという仕様上の特徴があります。

そのため、以下のような状態が発生します。

  • 記事内に画像がしっかり表示されており、読者から見ても問題なく公開されている。
  • しかし、裏側のデータベース上では親ID(post_parent)が0(未添付)のままで取り残されている。
  • メディアライブラリの「未添付」フィルターで検索すると、記事に使われているはずの画像が「未使用・未添付」としてヒットしてしまう。

この仕様により、記事をたくさん書いていくと「実際は記事で使っているのに、メディア管理上は未添付扱いになっている画像」が大量に発生し、整理や一括削除の妨げになる原因となります。

完成イメージ

実装手順

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

本文にファイル名を含む文言がある場合、意図しない記事に紐付くことがあります。

// 一括操作を追加
add_filter('bulk_actions-upload', function ($bulk_actions) {
  $bulk_actions['attach_by_content'] = '自動で投稿に添付';

  return $bulk_actions;
});


// 一括操作を実行
add_filter('handle_bulk_actions-upload', function ($redirect_to, $action, $post_ids) {

  if ($action !== 'attach_by_content') {
    return $redirect_to;
  }

  // 権限チェック
  if (!current_user_can('upload_files')) {
    wp_die('この操作を実行する権限がありません。');
  }

  $attached_count = 0;

  foreach ($post_ids as $attachment_id) {
    $attachment_id = absint($attachment_id);
    $attachment    = get_post($attachment_id);

    // 対象外の場合
    if (!$attachment || $attachment->post_type !== 'attachment' || $attachment->post_parent !== 0) {
      continue;
    }

    // 添付ファイルのパスを取得
    $file_path = get_attached_file($attachment_id);

    if (!$file_path) {
      continue;
    }

    // 拡張子を除いたファイル名を取得
    $base_name = pathinfo($file_path, PATHINFO_FILENAME);

    if ($base_name === '') {
      continue;
    }

    // 本文にファイル名を含む最新の公開投稿を取得
    $target_post_id = my_find_post_by_filename($base_name);

    if (!$target_post_id) {
      continue;
    }

    // 投稿に添付
    $result = wp_update_post([
      'ID'          => $attachment_id,
      'post_parent' => $target_post_id,
    ], true);

    if (!is_wp_error($result)) {
      $attached_count++;
    }
  }

  // 通知を一時保存
  set_transient('my_admin_notice', [
    'type'    => 'success',
    'message' => $attached_count . ' 件の画像を、本文内で使用している最新の投稿に紐付けました。',
  ], 30);

  return $redirect_to;
}, 10, 3);


/**
 * ファイル名から本文に含まれる最新の投稿を取得
 *
 * @param string $base_name ファイル名(拡張子なし)
 * @return int 投稿ID
 */
if (!function_exists('my_find_post_by_filename')):
function my_find_post_by_filename($base_name) {
  global $wpdb;

  $like = '%' . $wpdb->esc_like($base_name) . '%';

  return (int) $wpdb->get_var(
    $wpdb->prepare(
      "
      SELECT ID
      FROM {$wpdb->posts}
      WHERE post_type = 'post'
        AND post_status = 'publish'
        AND post_content LIKE %s
      ORDER BY post_date DESC, ID DESC
      LIMIT 1
      ",
      $like
    )
  );
}
endif;


// 通知メッセージを出力
add_action('admin_notices', function () {
  if ($notice = get_transient('my_admin_notice')) {
    echo '<div class="notice notice-' . esc_attr($notice['type']) . ' is-dismissible"><p>' . esc_html($notice['message']) . '</p></div>';
    delete_transient('my_admin_notice');
  }
});

さいごに

今回紹介した方法を使えば、Gutenbergエディターの仕様によって「未添付」扱いになってしまった画像を、記事本文との照合によって一括かつ自動で適切な投稿へ紐付けることができます。
人手での煩雑な紐付け作業から解放され、メディアライブラリの管理効率が劇的に向上するため、ぜひ導入してみてください。

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