Advanced Custom Fields Proを用いたGutenbergのブロック開発

## Gutenbergが嫌われている件について

WordPress5.0にてGutenbergが搭載されてから早4年が経ちました。
今ではGutenbergをフル活用した「フルサイトエディター (FSE)」なるものが実装されつつあり、もはやWordPressを使用する人にとって、Gutenbergの理解は必須です。

しかし、、、世の中はGutenbergが嫌いな人が多い印象を抱きます:cry:

- Gutenberg内の機能開発が活発であり、運用の工数がかかる
- クラス名が勝手に割り振られてしまい、CSSでの制御が難しい
- アップデートによりブロックの見た目が崩れることがある
- Classic Editorよりも、投稿画面のカスタマイズがしにくい
- カスタムフィールドを設置しても、場所が見にくい（ページ下部とサイドバーにしか出せない）ので編集しにくい

などが主に嫌われる理由だと思います。

※余談ですが、フルサイトエディターの呼び方が**サイトエディター**に変更になりました。
より利用しやすい名前にしたことから、WordPress側はこの機能を本格的に開発していくことが予想されます。

https://make.wordpress.org/updates/2022/11/04/site-editor-a-more-user-friendly-name/

## じゃあ自分でGutenbergブロックを開発しちゃおう！

Gutenbergは幸いなことに、オリジナルブロックを開発することができます。

https://www.webdesignleaves.com/pr/wp/wp_block_basic_jsx.html

しかし、一つのブロックを作るのに途方もない苦労を要します。
Reactの知識やGutenbergのコアの仕組み等、考慮するべき点が非常に多く存在しているため、工数がかなりかかります。

そして何より、Gutenbergはまだ発展途上であり今後のアップデートによってはオリジナルブロックが壊れてしまうこともあるかもしれません。
Gutenbergのブロックが壊れてしまった場合の改修は、かなり大変なことになると予想されます。

## そんなあなたにはこれ「Advanced Custom Fields Pro」

https://www.advancedcustomfields.com/

かの有名なプラグイン「Advanced Custom Fields」の有料版「Advanced Custom Fields Pro（以下、ACFPro）」がGutenbergのブロック作成をサポートしてくれてました！

お値段：**Personalプラン（1つのサイト） $49/年**

:::note info
他にも無料で優秀なプラグインはたくさんありましたが、管理画面上での使い勝手・知名度・カスタマイズのしやすさ・ドキュメントの充実さ等を考慮すると、「ACFPro」が一番良いと考えられます。
安易に無料のプラグインを使用していると、いつサポートを切られるかわかりませんからね…
:::

:::note warn
ACFProのバージョンは6.0.3で行います。
6.x系から管理画面の見た目が大きく変更されました。後方互換性は一部例外（``parse_blocks()``を用いてACFブロックからデータを読み取る場合）を除きとれていますので、5.x系の方でも参考になるかと思います。
::: 

https://www.advancedcustomfields.com/blog/acf-6-0-released/

前置きが長くなりましたが、ここからACFProを用いたブロック開発の手順を公開していきます。

## ACFProを用いたブロック作成手順

### 実装したい機能

- カラム数を1-4まで自分で選択可能。
- カテゴリーを表示させるかどうか選択可能。
- 表示させたい投稿を手動で選択して、該当投稿のタイトル・サムネイルを表示する。
- 表示するスタイルを選ぶことができる

上記の機能を実装したカスタムブロックをACFProを使用して作成してみます。

### ①functions.phpの記述

```php:functions.php
<?php
/**
 * フロント側・エディター側の両方でjavascript,cssの読み込み
 */
function my_block_editor_front() {
  wp_enqueue_style(
    'block-style',
    home_url() . '/assets/css/wp/style.css', //ACFブロックで定義したスタイルはすべてこのCSSの中に入れる。
    [],
    '1.0.0'
  );
}
add_action('enqueue_block_assets', 'my_block_editor_front');

/**
 * ACFカスタムブロックの登録
 */
function acf_custom_block_add() {
if (function_exists('acf_register_block_type')){
    acf_register_block_type([
      'name' => 'post-list', //ブロック名を英数字で入力
      'title' => __('投稿一覧'), //ブロックの名前
      'description' => __('投稿一覧です。'), //ブロックの説明
      'icon' => 'admin-post', //ブロックのアイコンを指定 (https://developer.wordpress.org/resource/dashicons/#store)
      'keywords' => ['投稿一覧'], //ブロックを検索するときのキーワード
      'mode' => 'edit', //どのエリアにブロック入力欄を表示させるか
      // 'post_types' => array('post', 'page'), 表示する投稿タイプ（今回は使用しない）
      'category' => 'custom-layout-category', //ブロックをどのカテゴリーに属させるか
      'render_template' => 'acf-blocks/acf-block/post-list.php', // ベースとなるテンプレート
      'supports' => [
        'align' => false, // 中央寄せとか右寄せとかの表示を出さない。
        'multiple' => true, // ブロックを複数回利用させるかどうか
        // 'jsx' => true, // カスタムブロックの中で、Gutenbergのブロックを使用したい場合（今回は使用しない）
      ],
    ]);
  }
}

/**
 * ACFカスタムブロックの登録
 */
if (function_exists('acf_custom_block_add')) {
  add_action('acf/init', 'acf_custom_block_add');
}

/**
 * カスタムブロックカテゴリーの登録
 */
add_filter('block_categories_all', function ($categories) {
  $categories[] = [
    'slug' => 'custom-layout-category',
    'title' => 'ACFブロック',
  ];
  return $categories;
});
```

:::note info
ACFPro6.0系からblock.jsonを使用してACFのブロックを登録するようにできるようになりました。
:::

https://www.advancedcustomfields.com/resources/whats-new-with-acf-blocks-in-acf-6/


### ①-2 block.jsonを利用する場合（ACFPro6.0以降のみ）

```php:functions.php
<?php

/**
 * ACFカスタムブロックの登録（block.json利用）
 */
function register_acf_blocks() {
	register_block_type( __DIR__ . '/acf-block/post-list' ); // block.jsonが含まれているディレクトリへのパス
}
```

```json:block.json
{
    "name": "post-list",
    "title": "投稿一覧",
    "description": "投稿一覧です",
    "icon": "table-col-after",
    "category": "custom-layout-category",
    "align": "center",
    "apiVersion": 2,
    "acf": {
        "mode": "edit",
        "renderTemplate": "./acf-block/post-list/post-list.php"
    },
    "supports": {
      "align": false
    }
}

```

### ②ACFPro側の設定

![フィールドの設定](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2604490/360c22c2-e0d4-efe1-922e-c7d2df7ed1d0.png)

フィールドグループを表示する条件を「ブロック」、「投稿一覧（ブロック名）」に設定します。
※ブロック名は、functions.phpで設定した「title（今回の場合は投稿一覧）」を指定してください。

![フィールドの種類](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2604490/84cac7e2-f0da-acae-3f1b-97f4853452f8.png)

フィールドを設定します。色々設定する項目はありますが、作りたいブロックに合わせてフィールドを設定してください。

### ③フィールドで入力した項目を吐き出すテンプレートを作る。

①で```render_template```のパスを設定しましたので、そのファイルにテンプレートを書きます。

```php:post-list.php

<?php
/**
 * ブロック：関連投稿フィールド
 *
 * ブロック説明：
 * 関連投稿を表示させるフィールドです。
 * カテゴリー表示の有無、表示形式、カラム数などを選択できます。
 *
 *  @package shin_m
 */

// ACFに入力した値を取得
$col_num        = 'is-' . get_field( 'col-num' );
$type_field     = get_field( 'type' );
$featured_posts = get_field( 'featured_posts' );
$category_show  = get_field( 'category-show' );

if ( ! function_exists( 'my_category_html' ) ) {
	/**
	 * カテゴリーのHTMLを出力する関数
	 * function_existsで存在を確認しないと、Fatal error: Cannot redeclare ***（関数名）が出てしまう
	 *
	 * @param string $category_show カテゴリー表示フラグ
	 * @param Array  $featured_post 投稿一覧
	 * @return string カテゴリー名を返却
	 */
	function my_category_html( $category_show, $featured_post ) {
		if ( $category_show ) :
			$category_list = get_the_category( $featured_post->ID );
			foreach ( $category_list as $category ) :
				return $category->name;
			endforeach;
		endif;
	}
}
?>

<div class="grid <?php echo esc_html( $col_num ); ?>">
<?php
foreach ( $featured_posts as $featured_post ) :
	if ( 'col' === $type_field ) :
		?>

	<div class="grid-item">
		<a href="<?php echo esc_url( get_permalink( $featured_post->ID ) ); ?>">
			<img src="<?php echo esc_attr( wp_get_attachment_url( get_post_thumbnail_id( $featured_post->ID ) ) ); ?>">
			<span><?php echo esc_html( get_the_title( $featured_post->ID ) ); ?></span>
			<p><?php echo esc_html( my_category_html( $category_show, $featured_post ) ); ?></p>
		</a>
	</div>
		<?php
	elseif ( 'side' === $type_field ) :
		?>

	<div class="side_link">
		<a href="<?php echo esc_url( get_permalink( $featured_post->ID ) ); ?>">
			<p class="side_img">
				<img src="<?php echo esc_attr( wp_get_attachment_url( get_post_thumbnail_id( $featured_post->ID ) ) ); ?>">
			</p>
			<div class="side_info">
				<p class="side_title"><?php echo esc_html( get_the_title( $featured_post->ID ) ); ?></p>
				<p><?php echo esc_html( my_category_html( $category_show, $featured_post ) ); ?></p>
			</div>
		</a>
	</div>
		<?php
	endif;
endforeach;
?>

</div>
```


### ④投稿画面での表示

ブロックの設定が終わったので、記事投稿画面で編集してみましょう。

これまでの設定により、ブロック一覧に投稿一覧が出現しています。

![ブロック一覧](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2604490/9d48577f-9bf3-e422-551e-35173f870044.png)

クリックすると登録したフィールドがGutenberg上の編集画面に出現します。


![Gutenberg上の編集画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2604490/254f956e-666c-51d4-075f-892b94773686.png)

実際に入力してみましょう。

![作業シーン](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2604490/44d047b1-6395-afe6-230e-29973355edaa.gif)

タクソノミーを表示させなくしてみましょう。

![作業シーン2](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2604490/121ce183-8dd2-c3c1-3d24-f13a8a55cd99.gif)

見え方を変更してみましょう。

![作業シーン3](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2604490/1987872b-81f2-a407-6195-1e2db003c2c8.gif)

投稿を増やしてカラム数も増やしてみましょう

![作業シーン](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2604490/91269fb9-d1d2-b631-cfc4-1bcbac649d0d.gif)


## でもこれなら普通のGutenbergのブロックでいいのでは？？

クエリーループブロックを使用すれば、今回のようなことは出来なくないですから、そう思ってしまうこともあると思います。


しかし、開発者である私たちはGutenbergのデフォルトの機能だけでは、クライアントの要望を果たすことができません。

例えば…
- スライド機能を記事の中で実装させたい
- モーダル機能を実装したい
- 緯度と経度を入れたらGoogleマップが出るように実装したい
- 表の特定のセルの背景色を変更したい
- 吹き出し型のブロックを作成したい
- ボタンにアイコンを設置したい
- アコーディオンブロックを設置したい
- 「もっと見る」のようなブロックを作成したい
- 簡単にオリジナルブロックを作成したい

など、数えればきりがないです。

これを可能にするのが、ACFProなのです。

## 参考サイト

https://yumegori.com/advanced-custom-fields-pro-making-original-blocks

https://flex-box.net/acf-innerblocks/

https://flex-box.net/acfblocks/

https://liginc.co.jp/560854