【WordPress】記事内の最初の画像をサムネイルとして取得する方法【全サイズ・srcset＆sizes対応版】

# 投稿内の最初の画像をアイキャッチにする

WordPressの記事一覧などで**『アイキャッチ画像が設定されていればそれを取得し、設定されていなければ投稿内の最初の画像を取得』**する方法は、調べればすぐ出てくる。

　→　[WordPressで記事内の最初の画像をアイキャッチ代わりに使う方法](https://idotdesign.net/blog/web/wordpress/first-image-for-eyecatch/)

　→　[記事内の一番最初の画像を取得してサムネイル画像表示](http://detarame.moo.jp/2010/12/17/%E8%A8%98%E4%BA%8B%E5%86%85%E3%81%AE%E4%B8%80%E7%95%AA%E6%9C%80%E5%88%9D%E3%81%AE%E7%94%BB%E5%83%8F%E3%82%92%E5%8F%96%E5%BE%97%E3%81%97%E3%81%A6%E3%82%B5%E3%83%A0%E3%83%8D%E3%82%A4%E3%83%AB%E7%94%BB/)
>```php:function.php
function catch_that_image() {
    global $post, $posts;
    $first_img = '';
    ob_start();
    ob_end_clean();
    $output = preg_match_all('/<img.+src=[\'"]([^\'"]+)[\'"].*>/i', $post->post_content, $matches);
    $first_img = $matches [1] [0];
>
if(empty($first_img)){ //Defines a default image
        $first_img = "http://detarame.moo.jp/wp/wp-content/themes/whbk5/images/default.jpg";
    }
    return $first_img;
}
```
>
```php:example.php
<?php if (has_post_thumbnail()) : ?>
	<?php the_post_thumbnail('thumbnail'); ?>
<?php else : ?>
	<img src="<?php echo catch_that_image(); ?>" alt="<?php the_title(); ?>" />
<?php endif ; ?>
```

しかし、このやり方では投稿内の画像サイズの設定もそのまま反映されてしまうため、サイズ設定の指定や出し分けができない上に、**レスポンシブにきちんと対応できない**のが一番の難点。

「CSSで`width`と`height`をパーセント指定にしたら？」と言われるかもしれないところだが、
自分のブログならそれでもいいが成果物としてクライアントに納品する場合、投稿内の画像のサイズ設定や元サイズなんて指定できないため、
幅が100～200px程度のアイコンのような画像が投稿内の最初の画像だった場合、`width: 100%;`とかにしてると、画像が酷くぼやけて見えてしまう。
CSSでの表示は`max-width`の指定のみにとどめ、`wp_get_attachment_image`を使ってサイズを指定し、さらに`srcset`と`sizes`でデバイスごとに適切に表示画像を出し分けたい。

そのためには、

1. 投稿内の画像から画像IDを取得
2. `wp_get_attachment_image`で出力
3. `srcset`と`sizes`を指定する

という手順を踏んで出力する必要がある。

## 投稿内の画像からIDを取得
投稿ビジュアルエディタで、メディアライブラリから画像を挿入した場合、以下のコードになる。

```html
<img class="wp-image-*** size-(画像サイズ)" src="(画像url)" alt="***" width="***" height="***" />
```

`wp-image-***`というクラスが付与されるが、***部分がIDなので、それを引っ張ってくればよい。

```php
preg_match_all( '/<img.+class=[\'"].*wp-image-([0-9]+).*[\'"].*>/i', $post->post_content, $matches );
```
これで、投稿内の画像IDを`$matches`で取得できる。

## `srcset`と`sizes`を指定
`srcset`と`sizes`は、WordPress4.0以降ならそれぞれ`wp_get_attachment_image_srcset`と`wp_get_attachment_image_sizes`という関数が用意されているので、それを使う。

[wp_get_attachment_image_srcset() | Function | WordPress Developer Resources](https://developer.wordpress.org/reference/functions/wp_get_attachment_image_srcset/)

[wp_get_attachment_image_sizes() | Function | WordPress Developer Resources](https://developer.wordpress.org/reference/functions/wp_get_attachment_image_sizes/)

この関数、なぜか日本語の情報が異常に少なく（それどころか日本語記事だと**srcsetとsizesを削除する方法ばっかり出てくる**）、日本語訳のリファレンスすらない。
投稿画像をテーマ内で呼び出してかつレスポンシブ対応させる場合は、ほとんど必須と言ってもいいと思うのだけど…

### `wp_get_attachment_image_srcset`と`wp_get_attachment_image_sizes`

どちらも`( $attachment_id, $size, $image_meta )`の3つの引数を取る。
リファレンスによると以下の通り。

|パラメータ|説明|初期値|
|:-:|:-:|:-:|
|`$id`|画像のID|（なし）|
|`$size`|画像のサイズ。<br>`'medium'`などの登録されている画像サイズ<br>（`add_image_size`で追加したものも含む）か、<br>幅と高さの数値の配列（単位はpxになる）のどちらか。|`'medium'`|
|`$image_meta`|`wp_get_attachment_metadata()`で返される<br>画像のメタ情報。…らしい。|`null`|

基本的には、IDと画像サイズを指定すれば、それに対応した`srcset`（`sizes`）の値を出力してくれる、という認識でよい。
`$image_meta`が正直かなり謎で、コードを追う限り実際の返り値には一切影響していないように見えるため全く必要なさそうなんだが、一体何なんだろうか……今回のコードでは`null`のままでも問題なく動作したので説明を省くことにする。


で、`wp_get_attachment_image`なら属性値を好きに追加することができるので、
上記関数で取得した値をそれぞれ配列で指定してあげればOK。


# 実際に出力
function.phpに、下記を追記する。

```php:function.php
// $get_size: 取得する画像のサイズ
// $altimg_id: 代替画像のID。（画像はあらかじめメディアライブラリからアップロードしておく）
//             nullの場合、投稿内に画像が無ければ何も出力しない
function catch_thumbnail_image($get_size = 'thumbnail', $altimg_id = null) {
	global $post;
	$image = '';
	$image_get = preg_match_all( '/<img.+class=[\'"].*wp-image-([0-9]+).*[\'"].*>/i', $post->post_content, $matches );
	$image_id = $matches[1][0];
	if( !$image_id && $altimg_id ){
		$image_id = $altimg_id;
	}
	$image = wp_get_attachment_image( $image_id, $get_size, false, array(
		'class' => 'thumbnail-image',
		'srcset' => wp_get_attachment_image_srcset( $image_id, $get_size ),
		'sizes' => wp_get_attachment_image_sizes( $image_id, $get_size )
	) );
	if( empty($image) ) {
		$image = '';
	}
	return $image;
}
```

出力したい場所に以下のコードを挿入。

```php:example.php
<?php
$catch_thumbnail_size = 'thumbnail';
if(has_post_thumbnail()){
	the_post_thumbnail($catch_thumbnail_size);
} else {
	echo catch_thumbnail_image($catch_thumbnail_size);
}
?>
```

これで、

1. アイキャッチ画像が設定してある場合はそれを取得し出力
2. アイキャッチは無いが投稿内に画像がある場合は、そのうちの1番最初の画像を取得し出力
3. それも無い場合は代替画像を出力 or 何もしない

という動作を

- 投稿内画像のサイズ設定に影響されない
- 自由に出力サイズを指定
- `srcset`と`sizes`も出力し、デバイスサイズに合わせた画像を表示させる

という条件で実現できる。