はじめに
WordPressのメディアライブラリには「未添付(Unattached)」というフィルターがあります。
しかし、これは実際の使用状況を反映していない場合があります。
例えば、アイキャッチ画像や本文中に画像が使われていても「未添付」として表示されることがあります。
そこで、本当に使われていない画像(未使用画像)だけをフィルタリングをメディア一覧に追加します。
この記事では、この方法について説明します。
WordPress標準の「未使用(未添付)」との違い
WordPress標準の未添付フィルターは単に投稿との「関連(添付)」があるかどうかで判定されるため、実際に使われているかどうかとは無関係です(表1参照)。
今回のカスタマイズは実際の使用実態に基づいたフィルターですので、精度が大きく向上します。
| 比較項目 | WordPress標準の「未添付」 | 今回の自作フィルター |
|---|---|---|
| 判定基準 | 投稿などに添付されていないメディア | 投稿本文・アイキャッチで使われていない画像 |
| アイキャッチ対応 | 無視される | 検出対象 |
| 本文中の画像URL対応 | 検出されない | URLから逆引きして検出 |
| カスタムフィールド | 非対応 | 現状未対応(拡張可能) |
| 処理速度 | 高速 | 投稿数に比例してやや重くなる |
| 精度 | 誤検出あり(使われてても未添付になる) | 高精度(実使用の有無で判定) |
完成イメージ

実装手順
以下のコードをfunctions.phpに追加します。
// 一覧にフィルターボタンを追加
add_action('restrict_manage_posts', function () {
global $pagenow;
// メディア一覧以外は処理対象外
if ($pagenow === 'upload.php') {
$selected = $_GET['media_usage'] ?? '';
?>
<select name="media_usage">
<option value="">使用状況で絞り込み</option>
<option value="unused" <?php selected($selected, 'unused'); ?>>未使用</option>
</select>
<?php
}
});
// 投稿クエリの取得条件を変更
add_filter('pre_get_posts', function ($query) {
if (
is_admin() &&
$query->is_main_query() &&
$query->get('post_type') === 'attachment' &&
isset($_GET['media_usage']) &&
$_GET['media_usage'] === 'unused'
) {
// 未使用のメディアIDを取得
$unused_ids = get_unused_attachment_ids();
// 未使用のメディアIDを設定
$query->set('post__in', $unused_ids ?: [0]);
}
});
/**
* 使用中のメディアID一覧を取得
*
* @return int[] 使用中のメディアID一覧
*/
if (!function_exists('get_used_attachment_ids')):
function get_used_attachment_ids() {
global $wpdb;
$used_ids = [];
// アイキャッチ画像のIDを取得
$thumb_ids = $wpdb->get_col("
SELECT DISTINCT meta_value
FROM {$wpdb->postmeta}
WHERE meta_key = '_thumbnail_id'
AND meta_value != ''
");
if ($thumb_ids) {
$used_ids = array_merge($used_ids, $thumb_ids);
}
// 親投稿に紐づくメディアIDを取得
$parent_ids = $wpdb->get_col("
SELECT ID
FROM {$wpdb->posts}
WHERE post_type = 'attachment'
AND post_parent > 0
");
if ($parent_ids) {
$used_ids = array_merge($used_ids, $parent_ids);
}
// 投稿本文を取得
$raw_contents = $wpdb->get_col("
SELECT post_content
FROM {$wpdb->posts}
WHERE post_status NOT IN ('trash', 'auto-draft')
AND post_content LIKE '%/wp-content/uploads/%'
");
$found_urls = [];
foreach ($raw_contents as $content) {
if (preg_match_all('#https?://[^\s"\']+?/wp-content/uploads/[^\s"\'\[\]]+#i', $content, $matches)) {
foreach ($matches[0] as $url) {
$found_urls[] = $url;
}
}
}
// 重複を除いた画像URLを取得
if (!empty($found_urls)) {
$unique_urls = array_unique($found_urls);
foreach ($unique_urls as $url) {
$clean_url = strtok(rtrim($url, ']'), '?');
// リサイズ画像を元画像URLに変更
$original_url = preg_replace('/-\d+x\d+(\.[a-zA-Z0-9]+)$/', '$1', $clean_url);
$id = attachment_url_to_postid($original_url);
if (!$id && $original_url !== $clean_url) {
$id = attachment_url_to_postid($clean_url);
}
if ($id) {
$used_ids[] = $id;
}
}
}
return array_values(array_unique(array_map('intval', array_filter($used_ids))));
}
endif;
/**
* 未使用のメディアID一覧を取得
*
* @return int[] 未使用のメディアID一覧
*/
if (!function_exists('get_unused_attachment_ids')):
function get_unused_attachment_ids() {
global $wpdb;
// 全メディアIDを取得
$all_ids = $wpdb->get_col("
SELECT ID
FROM {$wpdb->posts}
WHERE post_type = 'attachment'
");
// 使用中のメディアIDを取得
$used_ids = get_used_attachment_ids();
// 未使用のメディアIDを取得
return array_values(array_diff(array_map('intval', $all_ids), $used_ids));
}
endif;
参考:メディアの添付関係について
本文にメディアを追加した場合でも未使用と判定されることがあります。
WordPressの内部的には、メディア(添付ファイル)は「投稿ID(親)」と関連付けられて保存されます。
しかし、エディタで画像を挿入しても、「投稿に添付される」とは限りません(表2参照)。
| 画像の挿入方法 | 添付関係 |
|---|---|
| 投稿画面でメディアをアップロードし、そのまま挿入 | あり(親投稿に紐づく) |
| メディアライブラリから既存画像を選んで本文に挿入 | なし(添付されない) |
| 画像URLを直接コピー&ペーストで本文に貼り付け | なし |
さいごに
今回紹介した方法を使えば、メディアライブラリから本当に使われていない画像だけを正確に抽出できます。
WordPressの標準機能では判定できないアイキャッチや本文埋め込みの画像も考慮されるため、高精度で不要画像を検出できます。
画像の整理や容量削減、メディア管理の効率化に役立つ便利なカスタマイズですので、ぜひご活用ください。ます。

