WordPressのACFで動的セレクトリストを作るーその②

WordPressのAdvanced Custom Field(ACF)プラグインで動的セレクトリストを作るの続きです。

大カテゴリを選択するとその配下の中カテゴリが選択できるように、中カテゴリを選択するとその配下の小カテゴリが選択できるようにするにはAjaxを使用します。

今回の記事はAjaxの実装から完了までになります。

この記事の範囲

・仕様

・カテゴリ構成

・ACFの設定

・初期表示処理

ここまでは①の記事を参照してください

・Ajaxで呼ばれる処理

・Ajaxを呼ぶ処理

・動作確認

・動かない場合に確認するところ

 

Ajaxで呼ばれる処理

以下のコードを function.php に追加してください

// 商品登録・編集画面で、中カテゴリ、小カテゴリを取得して返すAjax関数
function my_ajax_func(){
	// デフォルトの親カテゴリスラッグ名
	$parent = "item";

	// 選択されたカテゴリ(親カテゴリ)スラッグを取得
	if( array_key_exists('parent', $_REQUEST )) {
		$parent = $_REQUEST['parent'];
	}

	// カテゴリ一覧取得、スラッグは仕様上アルファベット小文字なので、小文字に変換
	$cateObj = get_category_by_slug( strtolower($parent) );
	$categories = get_categories( array(
		'parent' => $cateObj->term_id,
		'orderby' => 'term_order',
		'order' => 'ASC',
		'get' => 'all'							// このパラメータがないと、投稿記事が無いカテゴリは取得してこなくなる
	));

	// 取得したカテゴリ一覧を連想配列にする
	$list = array();
	foreach( $categories as $categorie ) {
		// スラッグは仕様上アルファベット小文字なので、大文字に変換する
		$list[strtoupper($categorie->slug)] = $categorie->name;
	}

	// リクエスト元に返す
    header("Content-Type: application/json; charset=utf-8");
    echo json_encode($list);
    wp_die();
}
add_action( 'wp_ajax_my_ajax', 'my_ajax_func' );
add_action( 'wp_ajax_nopriv_my_ajax', 'my_ajax_func' );

 

コード解説

4〜9行目:取得するカテゴリの親カテゴリを設定しています

 Ajaxを呼び出すときにパラメータとして親カテゴリのスラッグ名を設定します。

 設定されていない場合はスラッグ名「item」(カテゴリ名:商品)を親カテゴリとします

12〜18行目:設定した親カテゴリとするカテゴリを取得しています

 中カテゴリを取得する場合は選択された親カテゴリのスラッグ名を呼び出しパラメータに設定します

 カテゴリ取得オプションの ’get’ => ‘all’ が無いと記事がない(登録商品がない)カテゴリを取得してきませんので注意してください

20〜25行目:取得したカテゴリを スラッグ名:カテゴリ名 の連想配列にしています

27〜29行目:連想配列を呼び出し元に返しています

32〜33行目:アクションフック設定をしています

 

Ajaxを呼ぶ処理

まず、カスタムフィールドの表示オプションの「フィールドキー」をチェックしてフィールドキーが表示されるようにします。

このキーは後で使います。

 

Ajaxコードの記述

以下のコードを footer.php、管理画面の場合は admin-footer.php の </body> の直前に入れてください

<?php 
// 商品情報の登録・編集画面のみカテゴリプルダウン生成処理を入れる
global $post_type;

if(( $pagenow === 'post.php' || $pagenow === 'post-new.php' ) && $post_type == 'item' ) {
?>
    <script>
		// WordPressのAjaxファイルの所在を定義
        var ajaxurl = '<?php echo admin_url( 'admin-ajax.php'); ?>';

		function setCategorySelectList( selectTagID, parentCategorySlug, index0Title, selectItemID ) {
			jQuery.ajax({
					type: "POST",
					url: ajaxurl,
					cache: false,
					dataType:'json',
					data: { 
						'action': 'my_ajax',
						'parent' : parentCategorySlug,
					},
			}).done(function(data){
				// 正常取得
				// カテゴリを削除して、取得してきた中カテゴリを設定
				jQuery( selectTagID + ' > option').remove();
				jQuery( selectTagID ).append(jQuery('<option>').val('').text(index0Title));
				jQuery.each(data, function(index, value) {
                    var selected = false;
					
                    if( index == selectItemID ) {
                        selected = true;
                    }
					
                    jQuery( selectTagID ).append(jQuery('<option>').val(index).text(value).prop('selected', selected));
					// 設定した中、小カテゴリを表示させる
					jQuery( selectTagID ).parent().parent().removeClass( 'acf-hidden' );	
					jQuery( selectTagID ).parent().parent().removeAttr( 'hidden' );
					jQuery( selectTagID ).removeAttr( 'disabled' );
                });
			}).fail(function(XMLHttpRequest, textStatus, error){
				jQuery( selectTagID + ' > option').remove();
				jQuery( selectTagID ).append(jQuery('<option>').val('').text('情報の取得に失敗しました'));
			});

		}

		jQuery(document).ready(function($) {
			// selectタグのIDを設定
			var category1ID = "#acf-field_6237cbe216db8";	// 大カテゴリ
			var category2ID = "#acf-field_6237ccff16dba";	// 中カテゴリ
			var category3ID = "#acf-field_6237cd2616dbc";	// 小カテゴリ

			// 大カテゴリを変更した
			jQuery( category1ID ).on( "change",function() {
				setCategorySelectList( category2ID, jQuery( category1ID ).val(), "中カテゴリを選択してください", 0 );
				jQuery( category3ID + ' > option').remove();	// 小カテゴリもクリアする
				jQuery( category3ID ).parent().parent().addClass( 'acf-hidden' );	// 小カテゴリを非表示にする
			});

			// 中カテゴリを変更した
			jQuery( category2ID ).on( "change",function() {
				setCategorySelectList( category3ID, jQuery( category2ID ).val(), "小カテゴリを選択してください", 0 );
			});


			// 編集などの初期表示時にカテゴリが選択されている場合
			<?php
			$selectedCate1 = get_field('category1');
			$selectedCate2 = get_field('category2');
			$selectedCate3 = get_field('category3');

			if($selectedCate1 != "") {
				?>
				// 中カテゴリ初期表示設定
				setCategorySelectList( category2ID, "<?php echo $selectedCate1; ?>", "中カテゴリを選択してください", "<?php echo $selectedCate2; ?>" );
				<?php
			}

			if($selectedCate2 != "") {
				?>
				// 小カテゴリ初期表示設定
				setCategorySelectList( category3ID, "<?php echo $selectedCate2; ?>", "小カテゴリを選択してください", "<?php echo $selectedCate3; ?>" );
				<?php
			}
			?>
		});
    </script>
<?php } ?>

 

コード解説

5行目:ページが新規投稿(post-new.php)または投稿更新(post.php)の場合で、投稿タイプが商品(item)の場合にスクリプトを出力するようにしています。

9行目:WordPressの admin-ajax.php を使用するため、その所在を定義しています。

11〜44行目:Ajaxでカテゴリを取得してセレクトリストに設定している関数です

  引数は selectTagID : セットするセレクトタグIDです。中カテゴリに設定する場合は、中カテゴリのIDを指定します。

  parentCategorySlug : 親カテゴリスラッグです。中カテゴリを設定する場合は、選択されている大カテゴリのスラッグを指定します。

  index0Title : 未選択時に表示する内容です。「中カテゴリを選択してください」といった文言を設定します。

  selectItemID : 更新時などに選択されている項目のスラッグ名を指定します。

18行目: ’action’ で指定しているのが、上記の「Ajaxで呼ばれる処理」の32〜33行目で設定したアクションフック名になります

  アクションフックでは「wp_ajax_my_ajax」のようにしましたが「wp_ajax_」の部分を消した「my_ajax」だけを呼び出しに使用します

19行目:親カテゴリのスラッグ名をパラメータとして渡します

24行目:設定するセレクトリストの内容を空にします

25行目:未選択時の項目を設定します

26〜38行目:取得してきたカテゴリをセレクトリストに設定します

40〜41行目:取得に失敗した際のメッセージを設定しています

 

48〜50行目:セレクトタグのIDを設定しています

 ACFから生成されるタグには上記で調べたフィールドキーがIDとして設定されます

 タグIDは #acf-フィールドキー の形になります

 

53〜57行目:大カテゴリが変更された場合に中カテゴリを取得します

 同時に小カテゴリはクリアして、非表示にしています

 

60〜62行目:中カテゴリが変更された場合に小カテゴリを取得しています

67〜83行目:編集画面を開いたときにすでに設定されている中カテゴリ、小カテゴリを設定しています

 

動作確認

それでは、WordPressダッシュボードのメニューから 商品情報>新規登録 を表示してみましょう

まず、大カテゴリのセレクトリストには大カテゴリだけが表示されます

ここで「大カテゴリ1」を選択します

 

すると、中カテゴリには「大カテゴリ1」配下の中カテゴリだけがセレクトリストに表示されます

ここで「中カテゴリ11」を選択します

 

すると、小カテゴリには「中カテゴリ11」配下の小カテゴリだけがセレクトリストに表示されます

 

別のカテゴリでも正常に取得できていますね

 

以上で完了です

 

動かない場合に確認するところ

記事通りにやってんのに動かない!

って場合に確認する箇所を記載します。

初期表示のカテゴリ名は合っていますか?

大カテゴリに親カテゴリがないカテゴリが表示されてしまう場合、初期表示処理で function.php に追加したコードの4行目

get_cat_IDに指定しているカテゴリ名は合っていますか?

本記事内では「商品情報」というカテゴリを作成して、その下に大カテゴリを作成しました

ご自身のサイトの「大カテゴリ」の親カテゴリ名を指定してください

 

フィールド名は合っていますか?

本記事内では大カテゴリ、中カテゴリ、小カテゴリを category1、category2、category3 というフィールド名で使用しています。

ご自身のサイトのフィールド名に合わせてください。もしくは category1、category2、category3 に変更してください

 

アクションフック名、関数名は合っていますか?

function.php に記載したAjaxを呼び出すアクションフック名、関数名は合っていますか?

よくわからない場合は、記事に書いてあるコードのままにしておいてください

 

フィールドキーは合っていますか?

Ajaxコードの記述の48〜49行目に書いてあるフィールドキー(#acf-field_6237….)は私の環境のフィールドキーですので、そのままでは使えません

ご自身のサイトのフィールドキーを設定してください

フィールドキーの調べ方は本記事「Ajaxを呼ぶ処理」に記載しています。

 

最後に

久しぶりの記事がかなり長くなってしまいましたが、本記事を書いている最中にも別の案件でこの仕組が必要になりました

また今後必要になってくるかもしれない私自身へのメモ書きがてら記事にしました。

お役に立てれば幸いです。

 

2 件のコメント

  • こちらの内容、大変ありがたい記事でございました。3段階でカテゴリを絞り込みながら選択する方法を調べながら取り組んでいましたが、どうしても機能させることができず困っていたところ、こちらの内容を実装した結果、動作するようになりました!
    (当方の場合は、カテゴリーではなくタクソノミーでしたので、そのあたりは調整しました)

    1点、ご質問がございます。
    最後に選択された小カテゴリに基づいて、さらに、動的にの候補を絞り込む「投稿オブジェクト」方法を機能させる方法はお分かりになりますでしょうか。
    acf/fields/post_object/queryにて絞り込みを行っているのですが、どうしても小カテゴリの’スラッグを’parent’として取得できず。。。

    誠に勝手なお願いで大変恐縮でございますが、もし可能であれば、お知恵をいただけますと嬉しく思います。

    • Iwa さん

      記事を見ていただいたようでありがとうございます。
      お役に立てたようでなによりです。

      ご質問の件ですが、
      最後に選択された小カテゴリが設定されている投稿オブジェクトを取得したいということでしょうか?
      それとも、小カテゴリを親カテゴリとするカテゴリが設定されている(小カテゴリの下にあるカテゴリが設定されている)
      投稿オブジェクトを取得したいということでしょうか?
      それともぜんぜん違うことでしょうか?

  • コメントを残す

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