WordPress 添付ファイルの URL より添付ファイルの ID を取得

WordPress で添付ファイルの URL(URI) より添付ファイルの ID を取得する方法を紹介します。

標準の関数で用意してくれてもいいのではと思ったのですが、よくよく考えてみれば使うケースってほとんどないんですよね。

今回作成することになったのは、投稿の保存時にコンテンツ内の最初の添付ファイルをアイキャッチ画像に設定するためのプラグインを作成していて必要になったからです。通常、エディタで添付ファイルを追加した画像(img タグ)には、wp-image-{$id} クラスが付加されます。このクラス名より ID を取得することは可能なのですが、テーマやプラグインによってはこのクラスの出力を抑制しているものもあり、確実に ID を取得するために必要となったのです。

とりあえずググってみると、いくつかのサンプルコードを目にすることができましたが、いずれもある程度問題がありました。

ここでは、どのようなケースでも確実に取得する方法を紹介したいと思います。

GUID パラメーターを利用する方法(問題あり)

GUID パラメーターに URL が保存されていることを利用する方法です。

サンプルとしては下記のようなコードになります。

function get_attachmentid_by_url( $url ) {
	global $wpdb;

	$attachment_id = $wpdb->get_var( $wpdb->prepare(
		"SELECT ID FROM $wpdb->posts WHERE post_type='attachment' AND guid='%s' LIMIT 1;",
		$url
	) );
	return (int)$attachment_id;
}

この方法には問題があります。GUID パラメーターは作成時の URL であり URL の変更やメディアの編集等で同期しなくなります。

また、フィールド名からも分かるようにあくまでもグローバル一意識別子であり、たまたま URL が使用されているだけで、本来の目的とはちょっと違うかなと思います。

_wp_attached_file カスタムフィールドを利用する方法

_wp_attached_file カスタムフィールドを利用する方法です。

サンプルとしては下記のようなコードになります。

function get_attachment_id_by_url( $url ) {
	global $wpdb;

	$attachment_id = 0;
	$uploads = wp_upload_dir();
	$base_url = $uploads['baseurl'];
	if ( strpos( $url, $base_url ) === 0 ) {
		//$attached_file = ltrim( $url, $base_url . '/' );
		$attached_file = str_replace( $base_url . '/', '', $full_size_url );
		$attachment_id = $wpdb->get_var( $wpdb->prepare(
			"SELECT post_id FROM $wpdb->postmeta WHERE meta_key='_wp_attached_file' AND meta_value='%s' LIMIT 1;",
			$attached_file
		) );
	}
	return (int)$attachment_id;
}

_wp_attached_file カスタムフィールドには、アップロードディクレトリを基点とする相対パスでファイルのパスが保存されています。メディアの編集等を行っても、この値は更新されます。

なお、wp_attached_file カスタムフィールドは、フルサイズ(オリジナルサイズ)のパスとなります。

では、別サイズの場合はどうするかですが、初めに思いついたのは、_wp_attachment_metadata カスタムフィールドを使用する方法です。_wp_attachment_metadata カスタムフィールドには別サイズのファイル名を含むイメージ情報が含まれているのでこれを利用する方法です。しかし、この _wp_attachment_metadata にはメタ情報も含まれておりシリアライズされていることもあり確実ではありません。

では、別サイズのファイル名をフルサイズのファイル名に変換できればいいのですが、そのファイル名が別サイズなのかどうかはファイル名だけでは正しく判定することはできません。例えば “sample-150×150.jpg” の場合、別サイズの幅 150×高さ 150 のサイズの場合もありますが、元々の名前であるかもしれません。

では、どうするかというと初めにフルサイズとしてクエリーを実行し見つからなかったら、ファイル名をフルサイズのファイルに変換してみてクエリーを実行するという、2 回クエリーを実行するという方法になってしまいそうです。

別サイズ対応版

/**
 * 添付ファイルの URL より ID を取得します。
 *
 * @global wpdb $wpdb
 * @param string $url 添付ファイルの URL。
 * @param bool $is_full_size オプション。フルサイズかどうかを示す値。フルサイズの場合は true、別サイズの場合は false。デフォルトは true。
 * @return int 成功した場合は ID(1 以上)を、存在しない場合は 0 を返します。
 */
function get_attachment_id_by_url( $url, $is_full_size = true ) {
	global $wpdb;

	$attachment_id = 0;
	$full_size_url = $url;

	if ( ! $is_full_size ) {
		// URL から、サイズ表記(-999x999)を除去して、フルサイズの URL を取得する。
		$full_size_url = preg_replace( '/(-[0-9]+x[0-9]+)(\.[^.]+){0,1}$/i', '${2}', $url );
		if ( $url === $full_size_url ) {
			// 別サイズではないので中止。
			return $attachment_id;
		}
	}

	$uploads = wp_upload_dir();
	$base_url = $uploads['baseurl'];
	if ( strpos( $full_size_url, $base_url ) === 0 ) {
		//$attached_file = ltrim( $full_size_url, $base_url . '/' );
		$attached_file = str_replace( $base_url . '/', '', $full_size_url );
		$attachment_id = $wpdb->get_var( $wpdb->prepare(
			"SELECT post_id FROM $wpdb->postmeta WHERE meta_key='_wp_attached_file' AND meta_value='%s' LIMIT 1;",
			$attached_file
		) );
	}

	return (int)$attachment_id;
}

使い方

フルサイズの場合の例

$url = 'http://example.com/wp-content/uploads/2017/10/sample.jpg';
$attachment_id = get_attachment_id_by_url( $url );

フルサイズか別サイズか判別できない場合の例

$url = 'http://example.com/wp-content/uploads/2017/10/sample-150x150.jpg';
$attachment_id = get_attachment_id_by_url( $url );
if ( ! $attachment_id ) {
	$attachment_id = get_attachment_id_by_url( $url, false );
}

WP_Query 版

とりあえず前記のコードで動作には問題ないと思いますが、SQL クエリーではなく WP_Query を使用した方法も紹介しておきます。

/**
 * 添付ファイルの URL より ID を取得します。
 *
 * @global wpdb $wpdb
 * @param string $url 添付ファイルの URL。
 * @param bool $is_full_size オプション。フルサイズかどうかを示す値。フルサイズの場合は true、別サイズの場合は false。デフォルトは true。
 * @return int 成功した場合は ID(1 以上)を、存在しない場合は 0 を返します。
 */
function get_attachment_id_by_url( $url, $is_full_size = true ) {
	global $wpdb;

	$attachment_id = 0;
	$full_size_url = $url;

	if ( ! $is_full_size ) {
		// URL から、サイズ表記(-999x999)を除去して、フルサイズの URL を取得する。
		$full_size_url = preg_replace( '/(-[0-9]+x[0-9]+)(\.[^.]+){0,1}$/i', '${2}', $url );
		if ( $url === $full_size_url ) {
			// 別サイズではないので中止。
			return $attachment_id;
		}
	}

	$uploads = wp_upload_dir();
	$base_url = $uploads['baseurl'];
	if ( strpos( $full_size_url, $base_url ) === 0 ) {
		//$attached_file = ltrim( $full_size_url, $base_url . '/' );
		$attached_file = str_replace( $base_url . '/', '', $full_size_url );
		$query = new WP_Query( array(
			'fields' => 'ids',
			'post_type' => 'attachment',
			'post_status' => 'inherit',
			'meta_query' => array( array(
				'value' => $attached_file,
				'key' => '_wp_attached_file',
			) ),
		) );
		if ( ! empty( $query->posts ) ) {
			$attachment_id = $query->posts[0];
		}
	}

	return (int)$attachment_id;
}

最後に

フルサイズか別サイズか判定できない場合、最大 2 回のクエリーを実行する点はどうかなとは思いますが、他にいい方法が思いつきませんでした。他にいい方法があればコメントお願いします。

コメントを残す

メールアドレスが公開されることはありません。

日本語でコメントを入力してください。(スパム対策)