サポート » 使い方全般 » SettingAPI利用でデータを配列で保存する方法はある?

  • 解決済 sasakiharu

    (@sasakiharu)


    WordPress初心者です。
    よろしくお願いします。

    プラグイン作成の勉強中で、設定フォームを含む管理ページの作成をしています。
    セキュリティの面からもSettingAPI利用が良いかと考え、ひとまず1項目だけのデータ保存と取得までは成功しました。
    (add_settings_field、add_settings_section、register_setting、settings_fields、do_settings_sectionsの関数を使うパターンです)

    次に複数項目を試している中、参考サイトで「オプションテーブルに複数のエントリーを追加する場合は、配列として一つのエントリーにまとめるべき」という記事を見かけ、これを実行したいと考えましたが、参考にしたCodexの「設定ページの作成」「Settings API」には記述がなく、ブログにも無い様子です。
    ※Codexでも複数項目のサンプルがあり、それで試してみましたがデータベースで確認すると、やはり個々で保存されていました。

    教えて頂きたいのは、
    (1) SettingAPIを使い複数データを配列でまとめて1データとして保存する方法があるか?
    (2) あればその方法、なければ代替手法
    以上の2点です。

    また、初心者のため様々な記事の「こうすべき」を鵜呑みにしているところがあるため、「いや、ここはそれでなくても」などのご意見があればよろしくお願いします。

11件の返信を表示中 - 1 - 11件目 (全11件中)
  • こんにちは、sasakiharuさん。

    Form からのデータを配列で受け取り、DB へ保存する時は、シリアライズ化して保存すればよいです。
    保存されたデータを使用する時は、配列に戻してから使用します。

    is_serialized()maybe_serialize()maybe_unserialize() 関数を使用すればよいです。

    https://codex.wordpress.org/Function_Reference/is_serialized
    https://codex.wordpress.org/Function_Reference/maybe_serialize
    https://codex.wordpress.org/Function_Reference/maybe_unserialize

    これは、WordPress の質問というよりも、PHP な気がします。

    トピック投稿者 sasakiharu

    (@sasakiharu)

    KUCKLU様

    ありがとうございます!
    SettingAPI利用では複数データを配列でまとめて保存はできないということですね。

    早速参考にさせていただいて、maybe_serialize関数を内部で実行してwp_optionテーブルにデータ保存してくれるupdate_option関数で、テストは上手くいきました。

    内部処理が想像しにくいSettingAPI利用時より、FormからのPOSTデータの受け取りをひとつひとつ記述したので、私のような初心者には処理行程が見やすくなったのも勉強になりました。
    また、nonceチェック処理も試せたのも収穫でした。

    ありがとうございました!

    解決済みになっておりますが・・・

    SettingAPI利用では複数データを配列でまとめて保存はできないということですね。

    いえ、そうではなく、SettingAPI で管理画面を構築して、データを保存する時にシリアライズ化すればよいです。

    トピック投稿者 sasakiharu

    (@sasakiharu)

    KUCKLU様

    申し訳ありません。全くの勘違いだったんですね。
    (解決済みとしてしましたが、続けての投稿お許しを…)

    テストの際、CODEXの「Settings API」ページのサンプル通りの関数を使ってデータ保存まで確認しましたが、個々の関数内部の処理まで見ておらず、実際にはどの関数内でデータ保存が行われているか理解できていません。

    おっしゃられている「データを保存する時にシリアライズ化」は、これらの関数のうち内部で保存を行っているものをカスタマイズするということでしょうか?
    もしくは、内部で保存を行っている関数を利用せず、保存部分だけ別の記述を行うということでしょうか?

    いずれにせよ、関数の内部処理を見ていく必要があるということですね…。

    add_settings_field 関数の第三引数でコールバック関数を登録したと思いますが、その中でpostを配列で送れるように記述していけば良いと思います。

    こちらでいうと eg_setting_callback_function 関数を、

    function eg_setting_callback_function() {
    	$vals = get_option( 'eg_setting_name' );
    	$checked = isset( $vals[checkbox] ) && $vals['checked'] ? ' checked' : '';
    	$textVal = isset( $vals[checkbox] ) ? $vals['checked'] : '';
    	echo '<input name="eg_setting_name[cb]" id="gv_thumbnails_insert_into_excerpt" type="checkbox" value="1" class="code" ' . $checked . ' /> Explanation text';
    	echo '<input name="eg_setting_name[text]" id="gv_thumbnails_insert_into_something" type="text" value="' . esc_attr( $textVal ) . '" />';
     }

    という感じに粛々とフォーム要素を記述していく感じですね。(未検証)

    あっ、すみません。Code 間違えてますね。

    function eg_setting_callback_function() {
    	$vals = get_option( 'eg_setting_name', array() );
    	$checked = isset( $vals['cb'] ) && $vals['cb'] ? 'checked="checked"' : ''; // fix
    	$textVal = isset( $vals['text'] ) ? $vals['text'] : ''; // fix
    	echo '<input name="eg_setting_name[cb]" id="gv_thumbnails_insert_into_excerpt" type="checkbox" value="1" class="code" ' . $checked . ' /> Explanation text';
    	echo '<input name="eg_setting_name[text]" id="gv_thumbnails_insert_into_something" type="text" value="' . esc_attr( $textVal ) . '" />';
     }

    (コメント箇所以外もどさくさに紛れていくつか直してます ^_^;)

    ちなみにmaybe_unserialize、およびmaybe_serializeは、この場合、それぞれget_optionupdate_option内で適用されているので考慮しなくても大丈夫だと思います。

    やっと検証しましたが問題はないようです。
    ただし体裁はひどいものです。

    一応下記でそれなりにはなります。

    function eg_setting_callback_function() {
    	$vals = get_option( 'eg_setting_name', array() );
    	$checked = isset( $vals['cb'] ) && $vals['cb'] ? 'checked="checked"' : '';
    	$textVal = isset( $vals['text'] ) ? $vals['text'] : '';
    	echo '<input name="eg_setting_name[cb]" id="gv_thumbnails_insert_into_excerpt" type="checkbox" value="1" class="code" ' . $checked . ' /> Explanation text';
    	echo '</td></tr><tr><th>Text</th><td>'; // add
    	echo '<input name="eg_setting_name[text]" id="gv_thumbnails_insert_into_something" type="text" value="' . esc_attr( $textVal ) . '" />';
    }

    …かなりブサイクなコードですが – -;

    ちなみに、Settings APIでは add_settings_sectiondo_settings_sectionsはすっ飛ばしてpage直下にfieldを展開することもできます。
    Codexのように既存のsectionに追加する場合は(多分)上記のような手法をとることになると思いますが、add_menu_pageadd_submenu_page(などそれに類する関数)から設定ページを構築する場合はそちらのほうが良いかもしれませんね。

    以上あしからず

    遅くなりすみません。

    register_setting() 関数の第 3 引数に CB 関数を指定できるので、そこでデータのサニタイズ処理を行い、return してあげれば、配列の場合は勝手にシリアライズ化され、データベースに保存されます。

    Settings API を使用していれば、配列のデータは自動でシリアライズ化してくれます。get_option でオプションを取得する時は配列に戻してくれるので、サニタイズ処理以外は意識する必要はないと思います。
    自前で、何かする時は、is_serialized()、maybe_serialize()、maybe_unserialize() 関数を利用するとよいでしょう。
    ※ 前の回答ですと、Settings API でも自前でシリアライズ化するみたいな回答でした。
    ※ 言葉足らずですみません。

    register_setting( 'example_settings', 'my_example_options', function ( $settings ) {
        // サニタイズ処理
        // ここで var_dump($settings) をして、値が配列ではない場合、HTML の input タグの書き方を見なおしてみてください。
    
        return $settings;
    } );

    ※ サンプルでは、無名関数を使用していますのでご注意ください。

    サンプルコードです。
    自己責任でお願いします。

    /**
     * Dummy settings
     *
     * @return array
     */
    function _my_dummy_settings() {
    	return array(
    		'group1' => array( 0, 0, 0, 0 ),
    		'group2' => array( 0, 0, 0, 0 )
    	);
    }
    
    /**
     * Default settings
     *
     * @return array
     */
    function _my_default_settings() {
    	$args = array(
    		'group1' => array( 0, 1, 0, 1 ),
    		'group2' => array( 1, 0, 1, 0 )
    	);
    
    	return wp_parse_args( apply_filters( 'my_default_settings', array() ), $args );
    }
    
    /**
     * Get settings
     *
     * @return array
     */
    function my_get_option() {
    	return wp_parse_args( get_option( 'my_example_settiongs' ), _my_default_settings() );
    }
    
    /**
     * Add admin menu
     * @return void
     */
    function my_admin_menu() {
    	add_options_page( __( 'Example Settings', 'MyTextDomain' ), __( 'Example Settings', 'MyTextDomain' ), 'manage_options', 'my_plugin_example_settiongs', '_my_admin_page' );
    }
    add_action( 'admin_menu', 'my_admin_menu' );
    
    /**
     * Add admin page
     *
     * @return string
     */
    function _my_admin_page() {
    	?>
    	<div class="wrap">
    		<h2><?php _e(  'Example Settings', 'MyTextDomain' ); ?></h2>
    		<form method="post" action="options.php" novalidate="novalidate">
    			<?php
    			settings_fields( 'my_example_settiongs' );
    			do_settings_sections( 'my_example_settiongs' );
    			submit_button();
    			?>
    		</form>
    	</div>
    	<?php
    }
    
    /**
     * Add admin page init
     * @return void
     */
    function my_admin_init() {
    	register_setting( 'my_example_settiongs', 'my_example_settiongs', function ( $settings ) {
    		if( is_null( $settings ) ) return _my_dummy_settings();
    
    		foreach ( $settings as $group ) {
    			foreach ( $group as $key => $value ) {
    				$settings[$group][$key] = ( 1 === (int)$value ) ? (int)$value : 0;
    			}
    		}
    
    		return $settings;
    	} );
    
    	add_settings_section( 'my_example_settiong_sections', null, null, 'my_example_settiongs' );
    
    	add_settings_field(
    		'my_setting_g1i1',
    		__( '[Group 1] Item 1', 'MyTextDomain' ),
    		function () {
    			$settings = _my_dummy_settings();
    			$options  = my_get_option();
    			$option   = $options['group1'];
    
    			foreach ( $settings['group1'] as $key => $value ) :
    				?>
    				<label><input type='checkbox' name='my_example_settiongs[group1][<?php echo (int)$key; ?>]' <?php checked( (int)$option[$key], 1 ); ?> value='1' /><?php _e( 'Item ' . $key, 'MyTextDomain' ); ?></label>
    				<?php
    			endforeach;
    		},
    		'my_example_settiongs',
    		'my_example_settiong_sections'
    	);
    	add_settings_field(
    		'my_setting_g2i1',
    		__( '[Group 2] Item 1', 'MyTextDomain' ),
    		function () {
    			$settings = _my_dummy_settings();
    			$options  = my_get_option();
    			$option   = $options['group2'];
    
    			foreach ( $settings['group2'] as $key => $value ) :
    				?>
    				<label><input type='checkbox' name='my_example_settiongs[group2][<?php echo (int)$key; ?>]' <?php checked( (int)$option[$key], 1 ); ?> value='1' /><?php _e( 'Item ' . $key, 'MyTextDomain' ); ?></label>
    				<?php
    			endforeach;
    		},
    		'my_example_settiongs',
    		'my_example_settiong_sections'
    	);
    }
    add_action( 'admin_init', 'my_admin_init' );
    トピック投稿者 sasakiharu

    (@sasakiharu)

    KUCKLU様
    mimosafa様

    お二方とも、丁寧な回答ありがとうございます!
    私の方が回答の確認がしばらくできず、返信遅くなり申し訳ありません。
    検証までして頂いて、感謝しております。
    早速参考にさせて頂きます!

    とうの昔に解決済みの質問ですが、

    #

    ちなみに、Settings APIでは add_settings_sectionとdo_settings_sectionsはすっ飛ばしてpage直下にfieldを展開することもできます。

    add_settings_sectiondo_settings_sections は必須ですね
    完全に誤認識でした…

11件の返信を表示中 - 1 - 11件目 (全11件中)
  • トピック「SettingAPI利用でデータを配列で保存する方法はある?」には新たに返信することはできません。