Wonderful! WordPress

カスタムフィールドの投稿入力フォームへの表示

この記事は公開または最後に更新されてから2641日が経過しています。情報が古くなっている可能性があるのでご注意下さい。

とてもありがたい秀逸なプラグイン、カスタムフィールドテンプレートを使えば、入力フォームを投稿入力フォームやクイック編集フォームに表示してくれます。でも、ふとプラグインを使わなければ出来ないものだろうかと。
ネットで調べてみるとやはりありがたい情報を提供してくださるサイトが多々ありまして。

特に勉強させていただいたほんとにありがたいのは上記サイト。
そのほかにも色々と参考にさせていただきながら自分に都合の良いように作ってみました。

とりあえず、カスタムフィールドの情報を配列で定義しておくと後々使い勝手が良くて便利ということなのでそれを。
フォームとして出力する時に、データを判別して使えるような配列にしてあります。
配列のキーとしたのはカスタムフィールド名。最初のデータにform のinput ボックスのname 属性に使う文字列。POST送信される時にデータの判別子となる文字列です。 通常の投稿postとカスタム投稿diysでカスタムフィールドを使っているため、それぞれの投稿フォームにおいて出力するカスタムフィールド入力フォームを変更するために投稿の種類もデータに入れます。そのほうが後々追加することがあってもやりやすいのではないかと。

<?php
/*$meta_cf[ custom field name ] = array(
					0:form input name,
					1:取得データ形式('array':配列、'single':テキスト),
					2:Label 表示用 title,
					3:注釈文,
					4:input size,
					5:textareaの場合はtx,
					6:投稿種類(post=""(空) or カスタム投稿="diys")
				);*/
$meta_cf['firstID'] = array( 'cffirstID', 'single', 'First ID', '初アップの時 0 、既存の場合は初アップ時のpost ID', '25', '', '' );
$meta_cf['jpn'] = array( 'cfjpn', 'single', 'Japanese', '', '35', '', '' );
$meta_cf['rmn'] = array( 'cfrmn', 'single', 'Romanized', '', '35', '', '' ); 
$meta_cf['egn'] = array( 'cfegn', 'single', 'English', '', '35', '', '' );
$meta_cf['bon'] = array( 'cfbon', 'single', 'Binomial', '', '35', '', '' );
$meta_cf['take'] = array( 'cftake', 'single', '撮影日時', '', '25', '', '' );
$meta_cf['bcg'] = array( 'cfbcg', 'single', 'background image', '', '25', '', '' );
$meta_cf['fee'] = array( 'cffee', 'single', '餌付け状態', '1:餌付けされた野鳥<br>2:餌場に来ていた野鳥<br>3:人に育てられた鳥', '20', '', '' );
$meta_cf['enc'] = array( 'cfenc', 'single', '初見時状況', '0:否MFでの偶然の出会い<br>1:MFでの偶然の出会い<br>2:net等で情報を得て自力での出会い<br>3:MFで情報を得て自力での出会い<br>4:否MFで情報を得て自力での出会い<br>5:MFで情報を得て先客が発見<br>6:否MFで情報を得て先客が発見<br>7:net等で情報を得て先客が発見<br>8:先に情報があり自力で発見<br>9:先に情報があり先客が発見またはポイントが明確', '20', '', '' );
$meta_cf['up'] = array( 'cfup', 'single', 'Latest update', '', '25', '', 'diys' );
$meta_cf['desc'] = array( 'cfdesc', 'single', 'seo describe', '', '70', 'tx', 'diys' );
$meta_cf['sky'] = array( 'cfsky', 'single', 'seo keywords', '', '70', '', 'diys' );
?>
PHP
CopyExpand

※カスタムフィールドの情報の配列において、1:の「取得データ形式」について
カスタムフィールドは1つの投稿に対して、同じキーを持つ値を複数保存することができます。この場合データベース上( wp_postmeta テーブル)には同じpost_id 、同じmeta_key を持つデータが複数存在しています。
一方、add_post_meta() を使用すれば、1つのデータに配列の値を保存することもできます。この場合、そのデータは当然のことながらシリアライズされた状態で格納されています。シリアライズは説明がややこしいのでご自分で調べましょう。
そして、この「取得データ形式」の値というのは、いざ、カスタムフィールドのデータをget_post_meta( $post_id, $key, $single ) で取得する時に、その3つ目の引数を指定する際に使用する値です。
この3つ目の引数、$single を省略するかまたは false を指定すると戻り値は配列となります。仮にそのキーの値が1つしかなくても配列の状態になっています。ある値が配列で保存されていれば、アンシリアライズされた多次元の配列が返されます。
一方、$single をtrue に設定すれば、その戻り値は単純な1つの文字列です。そのキーの値が1つだけしかなければなんら支障はありません。しかし値が複数あったり、配列の値があったりした場合は、1つの文字列として1つのデータしか返してくれませんから、あれっ!ということになります。ですから、1つのキーに複数のデータを保存している場合には、この$single の引数には注意が必要ということになります。
ということで、全てのキーの値が1つしかないとわかっているなら、この「取得データ形式」の配列の’single’ の値は必要ありません。引数$single = true で固定してしまえばいいので。逆に全てのデータを$single を指定せず配列で取得して、ということでもいいので、どちらにしても全てのデータを同じ形式で取得するということにすれば、この値は必要ないということになります。

« add_meta_box()による新規セクションの登録 »

投稿の編集画面は「メタボックス」というセクションごとのボックスの組み合わせで構成されていて、独自のセクションを登録する場合にも、まずは新しくこのメタボックスを作成しなければならないということです。しかるにそのメタボックスの作成をする関数が、add_meta_box()で、この関数により記事投稿、ページ投稿、リンク編集画面にセクションを追加することができるということです。
で、その使い方はというと、

<?php
function add_mymeta_box(){
	add_meta_box( $id, $title, $callback, $post_type, $context, $priority, $callback_args );
}
add_action('add_meta_boxes','add_mymeta_box');
?>
PHP
CopyExpand

パラメータはというと、

$id(必須)メタボックスの HTML divタグの id 属性値
$title(必須)メタボックスのタイトル、画面上に表示される
$callback(必須)メタボックスの内容を表示する(HTML 出力する)関数名
$page(必須)メタボックスの表示先となるページの種類を指定('post', 'page', 'link', またはcustom_post_type。カスタムポストタイプはスラッグを指定)
$context
(オプション)
編集画面での表示場所 (normal, advanced または side) デフォルトは「advanced」
$priority
(オプション)
メタボックスが表示される優先度 (high, core, default, low) デフォルトは「default」
$callback_args
(オプション)
表示用関数のパラメータを指定(省略時はnull)

ここのサイトは通常投稿post とカスタム投稿diys でカスタムフィールドを使っているので、それぞれにこの関数が必要になるという事だと思います。ただし、html出力関数は同じものを使い分けることにします。

<?php
function add_cfpost_box() {
	if ( function_exists( 'add_meta_box' ) ) {
		add_meta_box( 'species_info', 'Species Information', 'cf_info_form', 'post','normal', 'high' );
		add_meta_box( 'diy_info', 'DIY Information', 'cf_info_form', 'diys', 'normal', 'high' );
	}
}
add_action( 'add_meta_boxes', 'add_cfpost_box' );
?>
PHP
CopyExpand

その、引数$callbackにて指定したhtml出力関数。

<?php
function cf_info_form() {
	global $post, $meta_cf;

	//CSRF対策の設定(フォームにhiddenフィールドとして追加するためのnonceを「'my_nonce」として設定)
	wp_nonce_field( wp_create_nonce(__FILE__), 'my_nonce' );
	$ptype = $post->post_type;
	if ( $ptype === 'post' ) {
        $ptype = '';
    }
	$divid = 'species_info';
	if ( $ptype === 'diys' ) {
		$divid = $ptype . '_info';
	}
	echo '<div id="' . $divid . '">';
?>
		<p>必要な情報を以下に入力してください。</p>
		<table class="cstflddl" style="margin-left:30px;">
<?php
			$curdata = '';
			foreach ( $meta_cf as $key => $val ) {
				if ( $ptype === $val[6] ) {// post typeにより出力するフィールドをかえる
					$true = ( $val[1] == 'single' ) ? true : false;
					$curdata = esc_html( get_post_meta( $post->ID, $key, $true ) );
					echo '<tr><td><label for="' . $val[1] . '">' . $val[2] . '</label></td>';
					if ( $val[5] === 'tx' ) {
						echo '<td><textarea name="' . $val[0] . '" rows="4" cols="' . $val[4] . '">' . $curdata . '</textarea></td>';
					} else {
						echo '<td><input type="text" name="' . $val[0] . '" value="' . $curdata . '"  size="' . $val[4] . '"></td>';
					}
					echo '<td><input type="checkbox" name="cb' . $val[0] . '"> : check delete data </td></tr>'."\n";
					if ( $val[3] !== '' ) {
						echo '<tr><td></td><td style="color:#808080">' . $val[3] . '</td></tr>'."\n";
					}
				}
			}
?>
		</table>
	</div>
<?php
}
?>
PHP
CopyExpand

6行目のwp_nonce_field()に関してはwordpressのcodexに詳細があります。セキュリティの為のもので、このサイトからの送信であることを確認するためのもの。この部分は参考にさせていただいたサイトの手法をそのまま使わせていただきました。
form の type="text" のinput フィールドは value="" でも空のデータを送信します。データが既存の場合はそれで削除するということでも良いのですが、あえて "checkbox" を設けて(29行目)、空のデータが送信された時は何もせず無視して、"checkbox" にチェックしたときだけデータを削除するということにしました。ちなみに"checkbox" はチェックしなければ送信されません。

« nonce(ノンス) に関して »

wordpressのcodexによるとnonce(ノンス or ナンス)は、

"nonce"はurlとformを悪用から保護するための
"number used once → 一度だけ使用する数字" です。
例えば、管理者ページにおいてポストNo.123を削除するこのようなURLを生成すると、URLの最後に"nonce"を含むことを確認できます。
https://example.com/wp-admin/post.php?post=123&action=trash&_wpnonce=b192fc4204
仮に誰かがポストNo.456を削除するためにURLを修正することを試みたとしても"nonce"は有効ではなく、試みは失敗します。
https://example.com/wp-admin/post.php?post=456&action=trash&_wpnonce=b192fc4204
無効な"nonce"によりwordpressはブラウザに"403 Forbidden"を送信し、"Are you sure you want to do this?"というエラーメッセージを表示させます。
<引用 - wordpress.org codex 訳:この記事の投稿者>

nonceフィールドはフォームの内容が現在のサイトから来たものであり、他のサイトからではないということを認証するために使われます。nonceは完全な保護を提供するものではありませんが、ほとんどの場合は有効な保護になります。フォーム内でnonceフィールドを使うのは重要です。
<引用 - wordpress codex 日本語版>

ということで、これはいわゆるワンタイムチケットということですね。

実は、『続、コメントの編集機能session版』においてsessionを使用したワンタイムチケットを実装したのですが、WordPressにはちゃんとそのための機能が実装されているというわけで、本来ならそれを使えば良かったということなのですね。またしても無知損でした。まぁ、いろいろと勉強になったし、ちゃんと機能しているようなのでそれはそれでいいかと。

< wp_nonce_field() >

実際にそのnonceフィールドを作成してくれる関数がwp_nonce_field()

wp_nonce_field( $action, $name, $referer, $echo )
PHP
CopyExpand

パラメータはというと、

$action(optional)アクション名、optionであるが指定する初期値:-1
$name(optional)nonce の名前、optionであるが指定する
*nonce にアクセスするにはこの名前を使う→ $_POST[$name]
初期値:"_wpnonce"
$referer(optional)認証用のrefereフィールドを設定するか否か。初期値:true
$echo(optional)hiddenフィールドをフォームに出力するか否か。
falseの場合はPHPの値として返す。
初期値:true

$echoをfalseに$refererをtrueにした場合はwp_referer_field()を使ってリファラーのフィールドを取得する必要があります。$refererをtrueにしnonceフィールドを出力した場合はリファラーフィールドも出力されます。
$actionと$nameはオプションですが、セキュリティを高めたい場合はこの二つのパラメータを設定することを推奨します。パラメータなしで関数を呼び出すのが簡単ですが、nonceがなんのパラメータも必要としなくなってしまうので、デフォルト値が何かを知っているクラッカーにとってあなたのnonceを推測して被害を与えるのが簡単になってしまいます。
<引用 - wordpress codex 日本語版>

ということです。で、実際に出力されるhiddenフィールドはだいたいこんな感じであろうとのこと。

<input type="hidden" id="_wpnonce" name="_wpnonce" value="796c7766b1" />
<input type="hidden" name="_wp_http_referer" value="/wp-admin/edit-comments.php" />
HTML
CopyExpand

< wp_create_nonce() >

wp_nonce_field()の第一引数、$actionに指定されているwp_create_nonce()

"nonce"を生成し、リターンする。"nonce"は、現在の時刻、引数$action、およびカレント・ユーザーIDに基づいて生成される。
Parameter $action : Action name. Should give the context to what is taking place. Optional but recommended.
<引用 - wordpress.org codex

<?php
	wp_create_nonce( $action );
?> 
PHP
CopyExpand

パラメータの$action においての説明が私には訳せません。
生成される"nonce"をより推定不能にするために、文字列または数字でユニークな値を指定すべきパラメータということのようです。ちなみにデフォルトは -1 。
私が参考にさせていただいたサイトの方式は、この$action パラメータに現在のファイルのフルパスを表す__FILE__を、nonceの名前に"my_nonce"を指定しているということになります。

<?php
	wp_nonce_field( wp_create_nonce(__FILE__), 'my_nonce' );
?>
PHP
CopyExpand

< wp_verify_nonce() >

そしてそのnonceが正しく設定されていて制限時間内かどうかを確かめるのがwp_verify_nonce()

<?php
	wp_verify_nonce( $nonce, $action );
?> 
PHP
CopyExpand

パラメータは、

$nonce(必須)認証したいフォーム内で使われたnonce、初期値なし
$action(optional)nonceを作成したときに指定したaction名。初期値:-1

パラメータ$nonce は送信された$_POST['my_nonce']で得られる値、$action には作成した時のwp_create_nonce(__FILE__)を指定するということになります。
ちなみに戻り値は、認証に失敗すれば false、成功すれば 1 または 2 の整数値で、

  • 1:過去12時間内に生成されたnonce
  • 2:過去12時間~24時間内に生成されたnonce ということです。

"nonce"に関しては以上で終了。

« 入力した値のデータベースへの保存 »

そして、入力したカスタムフィールド値をデータベースに保存しなければなりません。
投稿が保存される時のアクションフック "save_post" を使います。

<?php
function my_cfdata_save($post_id) {
	global $meta_cf;

	$curpost=get_post($post_id);
	$curtype=$curpost->post_type;

	//nonceを取得して認証を得る
	$my_nonce = isset($_POST['my_nonce']) ? $_POST['my_nonce'] : null;
	if(!wp_verify_nonce($my_nonce, wp_create_nonce(__FILE__))) {
		return $post_id;
	}
	//自動保存ルーチン(記事の自動保存処理)の場合は何もしない
	if(defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { return $post_id; }
	//ユーザーが編集権限を持っていない場合は何もしない。
	if(!current_user_can('edit_post',$post_id)) { return $post_id; }

	if($curtype== 'post' or $curtype== 'diys'){
		if($curtype==='post'){
			$curtype='';
		}
		foreach($meta_cf as $key=>$val){
			if($curtype===$val[6]){//投稿の種類によってフィールドをかえる
				$true=($val[1]=='single')?true:false;
				if(isset($_POST[$val[0]])){
					$meta_new=$_POST[$val[0]];

					$delkey='cb'.$val[0];
					if ( isset ( $_POST[ $delkey ] ) ) {
						$meta_cur = get_post_meta( $post_id, $key, $true);
						delete_post_meta( $post_id, $key, $meta_cur );
					} elseif ( $meta_new !== "" ) {
						$meta_cur = get_post_meta( $post_id, $key, $true );
						if ( $meta_cur === "" ) {
							add_post_meta( $post_id, $key, $meta_new, true );
						} elseif ( $meta_cur != $meta_new ) {
							update_post_meta( $post_id, $key, $meta_new );
						}
					}
				}
			}
		}
	}
}
add_action('save_post','my_cfdata_save');
?>
PHP
CopyExpand

html出力関数のところでも書きましたが、データの削除には"checkbox" を別に設けて、それにチェックのある時に実施し、空のデータが送信されてきても何もせず無視するようにしてあります。28行目の$delkeyがそれにあたります。
値の新規登録、変更、削除にはそれぞれWordPressにて用意されている関数を使用します。
空で無いデータが送信され、既存のデータが無い場合に"add_post_meta()" で新規データを登録していますが、これはわざわざ分岐させなくても"update_post_meta()" がやってくれるのでは、とどこかに書いてあったような気もしますが。

<?php
	add_post_meta( $post_id, $meta_key, $meta_value, $unique );
?>
PHP
CopyExpand
$post_id(必須)カスタムフィールドを追加する投稿の ID
$meta_key(必須)カスタムフィールドのキー
$meta_value(必須)カスタムフィールドの値
$unique(optional)キーをユニークにするかどうか。true のときは、指定した投稿に $meta_key を持つカスタムフィールドが無いときのみ追加し、すでにカスタムフィールドが存在していれば追加されません。
<?php
	delete_post_meta( $post_id, $key, $value );
?>
PHP
CopyExpand
$post_id(必須) 削除したいカスタムフィールドを持つ投稿の ID
$key(必須)削除したいカスタムフィールドのキー
$value(optional)削除したいカスタムフィールドの値。これは、同じキーを持つカスタムフィールドを区別するパラメータです。省略すると、指定したキーを持つカスタムフィールドはすべて削除されます。
<?php
	update_post_meta( $post_id, $meta_key, $meta_value, $prev_value );
?>
PHP
CopyExpand
$post_id(必須)カスタムフィールドを持つ投稿の ID
$meta_key(必須)更新したいカスタムフィールドのキー
$meta_value(必須)カスタムフィールドの新しい値
$prev_value(optional)更新したいカスタムフィールドの元の値。これは、同じキーを持つカスタムフィールドを区別するパラメータです。省略すると、指定したキーを持つカスタムフィールドはすべて値が更新されます。

これで投稿入力フォームにカスタムフィールドの入力フォームを表示することが出来たのですが、テストをしながらデータベースを見ていてちょっと気が付いたことがあります。
というのは、カスタムフィールドの値を保存しているデータベースのテーブルはwp_postmetaテーブルになるのですが、post_idフィールドの値が変更した投稿のpostIDの値で、フィールドmeta_valueがそれぞれ同じく1で、フィールドmeta_keyの値が_pingmeと_encloseme という二つのレコードが同時に追加されているようでした。
同じ投稿で変更を何度か行うと、その都度この二つのレコードが追加されて増えて行くようです。他にもpost_idフィールドに同じpostIDの値をもつレコードはあって、そのmeta_keyの値は_edit_last と_edit_lock の二つあるのですが、これらはなんとなく存在意味がわかるようですし、重複して増えていくことはありません。

さて、このmeta_keyの値が_pingmeと_encloseme というレコードは何のために作成されているのかというのがとても気になるところなのですが、日本語のページにおいてそれらの情報は見つけられませんでした。英語のサポートには若干あって、これがまたあまりわからないことなのですが、それによるとwp-cron によっていつか実行されるであろう処理の対象の目印として作成されているもので、処理が済めば自動的に削除されるであろうとのこと。
そもそもwp-cron がまったく接触したことのない未知の領域なのでさっぱりわかりません。
ためしに、ここで造った機能を無効にして、再びプラグインを起動させてやってみたところ結果は同じ。プラグインの内部を確認してみるとデータの変更には同じくupdate_post_meta() を使っていたのでそれもそのはず。いっそのことwordpress で用意されている関数を使わずに、sql で直接データを処理しようかとも思ったのですが、まぁ、wordpress が行っていることだし、いずれ自動的に削除されるということなのであまり気にせずにいたほうが良いように思いました。

Leave a Reply!

JavaScript is necessary to send a comment.
You can edit and delete your comment if you input a edit key.
Edit key is necessary for attesting you when you edit and delete it.
The tag of HTML cannot be used in comment.
When you comment for the first time, it is displayed after the approval of the administrator.
Because I cannot speak English so much, it takes time to answer.
Required fields are marked *.

※Please enter more than 5 characters only alphabets.
※Edit or delete are possible for 2000 days after approval.

*

♠Simplistic Comment User Editable v4.0

♠When visitors leave comments on the site this site collect the data shown in the comments form, and also the visitor’s IP address and browser user agent string to help spam detection.
♠This site does not use cookie when visitors leave comments and commenter edit comment.
♠This site uses Akismet to reduce spam. Learn how your comment data is processed.

Comments feed

Trackback URL : https://strix.main.jp/wp-trackback.php?p=48172

Sanbanse Funabashi
2011.01.01 sunrise

Top

スクロールさせるか画像をクリックすると元に戻ります。