wordpressはあまり触らないので、誤り、不足、余計な記述などあるかもしれません。
目的
単一のテキストを入力するカスタムフィールドを実装することを目的とする。シンプルにするため、本記事では文字列以外の型は扱わない。
前提
プラグインなしでWordPressにカスタム投稿タイプを実装したいで実装したカスタム投稿タイプ(info)にフィールドを追加するかたちで進めていく。
構成
今回触れるファイルは以下のように管理する。(それ以外は省略)
./wp-content/themes/mytheme
├── includes
│ └── info
│ ├── helper.php
│ ├── custom-post-type.php
│ └── single-custom-field.php
├── archive-info.php
├── single-info.php
└── functions.php
前回の記事から追加されたのはsingle-custom-field.php
とhelper.php
。
コード全体
single-custom-field.php
<?php
defined( 'ABSPATH' ) || exit;
require_once plugin_dir_path( __FILE__ ) . 'helper.php';
function register_info_single_custom_field() {
$c = new InfoSingleCustomField();
$key = $c->key;
$post_type = $c->type;
$default = $c->default;
// フィールドの振る舞いを定義する
$args = array(
'type' => 'string', // 値の型
'single' => true, // 単一の値を扱う
'default' => $default, // フィールドのデフォルト値
'show_in_rest' => true, // REST APIに含める
);
// カスタムフィールドを登録する
register_post_meta( $post_type, $key, $args );
}
add_action( 'init', 'register_info_single_custom_field' );
function add_info_single_custom_field_meta_box( $post_type ) {
$c = new InfoSingleCustomField();
$id = $c->id;
$type = $c->type;
// info以外の投稿タイプでは何もしない
// add_meta_box()でタイプを指定してるので不要かもしれない
if ( $post_type !== $type ) {
return;
}
add_meta_box(
$id, // フィールドのdiv要素に付与されるid
'カスタムテキストフィールド', // フィールドのラベル
'render_info_single_custom_field_callback', // フィールドのdomをレンダリングするコールバック
$type, // 対象の投稿タイプ
'normal', // コンテキスト
'default' // 表示優先度
);
}
add_action( 'add_meta_boxes', 'add_info_single_custom_field_meta_box' );
function render_info_single_custom_field_callback( $post ) {
$c = new InfoSingleCustomField();
$key = $c->key;
$nonce_name = $c->nonce_name;
$nonce_action = $c->nonce_action;
wp_nonce_field( $nonce_action, $nonce_name );
// trueを渡すと単一の値を取得する
$value = get_post_meta( $post->ID, $key, true );
?>
<div>
<label for="<?php echo( esc_attr( $key ) ); ?>">
テキスト
</label>
<input type="text"
id="<?php echo( esc_attr( $key ) ); ?>"
name="<?php echo( esc_attr( $key ) ); ?>"
value="<?php echo( esc_attr( $value ) ); ?>">
</div>
<?php
}
function save_info_single_custom_field_meta_box_data( $post_id ) {
$c = new InfoSingleCustomField();
$key = $c->key;
$post_type = $c->type;
$nonce_name = $c->nonce_name;
$nonce_action = $c->nonce_action;
// 投稿タイプをチェック
if ( get_post_type( $post_id ) !== $post_type ) {
return;
}
// ノンスチェック(ノンスが存在し、それが正しいか)
if ( !isset( $_POST[$nonce_name] ) || !wp_verify_nonce( $_POST[$nonce_name], $nonce_action ) ) {
return;
}
// 自動保存で更新を行わない
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return;
}
// 権限チェック
if ( !current_user_can( 'edit_post', $post_id ) ) {
return;
}
// データを取得
$new_value = isset( $_POST[$key] ) ? sanitize_text_field( $_POST[$key] ) : '';
// データを保存
update_post_meta( $post_id, $key, $new_value );
}
add_action( 'save_post', 'save_info_single_custom_field_meta_box_data' );
?>
helper.php
<?php
// スクリプトの不正な直接アクセスを防ぐ
defined( 'ABSPATH' ) || exit;
class InfoSingleCustomField {
// フィールドがレンダリングされるdiv要素のid
public $id = 'info_single_custom_field_container';
// フィールドを識別するキー
public $key = 'info_single_custom_field';
// フィールドを追加する投稿タイプの名前
public $type = 'info';
// フィールドに表示されるデフォルトの値
public $default = 'default value';
// ノンスの名前
public $nonce_name = 'info_single_custom_field_meta_box_nonce';
// ノンスのハッシュを生成するためのユニークな値
public $nonce_action;
function __construct() {
$this->nonce_action = basename( __FILE__ );
}
}
?>
single-info.php
<?php get_header(); ?>
<h1>info</h1>
<div>
<?php
global $post;
if ( !empty($post) ) :
?>
<div>
<h2><?php echo( $post->post_title ); ?></h2>
<small><?php echo( $post->post_date ); ?></small>
<div>
<small>
<?php
// メタデータの取得する
$post_meta = get_post_meta( $post->ID, 'info_single_custom_field', true );
echo( 'カスタムフィールド: ' . $post_meta );
?>
</small>
</div>
<div>
<?php print_r( $post->post_content ); ?>
</div>
</div>
<?php
endif;
?>
</div>
<?php get_footer(); ?>
実装
ヘルパー
保全性と安全性のため、多用する値は一箇所で定数として管理しておく。また、これらの値はグローバルな値として扱いたくないので、クラスのプロパティとして管理することにする。
もっと良い実装あったら知りたい。
それぞれの値の役割の詳細については都度触れる。
<?php
// スクリプトの不正な直接アクセスを防ぐ
defined( 'ABSPATH' ) || exit;
class InfoSingleCustomField {
// フィールドがレンダリングされるdiv要素のid
public $id = 'info_single_custom_field_container';
// フィールドを識別するキー
public $key = 'info_single_custom_field';
// フィールドを追加する投稿タイプの名前
public $type = 'info';
// フィールドに表示されるデフォルトの値
public $default = 'default value';
// ノンスの名前
public $nonce_name = 'info_single_custom_field_meta_box_nonce';
// ノンスのハッシュを生成するためのユニークな値
public function nonce_action() {
return basename(__FILE__);
}
?>
不正な実行を防ぐため、プロジェクト外からアクセスした場合は処理を終了する。
// スクリプトの不正な直接アクセスを防ぐ
defined( 'ABSPATH' ) || exit;
フィールドの本体実装
single-custom-field.php
に本体の実装を書いていく。
複数のカスタムフィールドを実装する場合、ここに書いていく関数名は簡単すぎると他の箇所と競合してしまうので、ユニークになるようにあえて冗長な名前にしてる。
helper.php
同様外部からのアクセスは処理を止める。
<?php
defined( 'ABSPATH' ) || exit;
helper.php
を読み込む。
<?php
defined( 'ABSPATH' ) || exit;
+ require_once plugin_dir_path( __FILE__ ) . 'helper.php';
メタデータを追加
register_post_meta
をinit
に追加して、カスタムフィールドのメタデータを投稿に登録する。
// つづき
function register_info_single_custom_field() {
$c = new InfoSingleCustomField();
$key = $c->key;
$post_type = $c->type;
$default = $c->default;
// フィールドの振る舞いを定義する
$args = array(
'type' => 'string', // 値の型
'single' => true, // 単一の値を扱う
'default' => $default, // フィールドのデフォルト値
'show_in_rest' => true, // REST APIに含める
);
// カスタムフィールドを登録する
register_post_meta( $post_type, $key, $args );
}
add_action( 'init', 'register_info_single_custom_field' );
register_post_meta()の引数の説明
1. $post_type
(必須)
投稿タイプの名前を渡す。投稿タイプ以外では'post', 'comment', 'term', 'user'などを渡す。空文字を渡した場合、全ての投稿タイプに対して適応される。
2. $key
(必須)
フィールドを識別するキー。
3. $args
(必須)
type: string
保存されるデータの型を指定。'string'、'boolean'、'integer'、'number'、'array'、'object'を使うことができる。デフォルトでは'string'。
single: bool
単一の値を扱う場合はtrue、複数の場合はfalseを渡す。デフォルトではfalse。
defaul: mixed
コントロールパネルで表示されるデフォルトの値。メタデータに何も保存されていない場合この値が返される。
show_in_rest: bool
REST APIで使用するためにはtrueを指定する。今回はブロックエディタに対応したいのでtrue。デフォルトではfalseなので注意。
default
に何かしらの値を指定しても、初期状態でメタデータには何も保存されない。
$args
に指定するパラメーターの詳細はregister_meta()
のドキュメントで確認する。
メタボックスを追加
投稿の編集ページにフィールドを表示するメタボックスを追加する。
// つづき
function add_info_single_custom_field_meta_box( $post_type ) {
$c = new InfoSingleCustomField();
$id = $c->id;
$type = $c->type;
// info以外の投稿タイプでは何もしない
// add_meta_box()でタイプを指定してるので不要かもしれない
if ( $post_type !== $type ) {
return;
}
add_meta_box(
$id, // フィールドのdiv要素に付与されるid
'カスタムテキストフィールド', // フィールドのラベル
'render_info_single_custom_field_callback', // フィールドのdomをレンダリングするコールバック
$type, // 対象の投稿タイプ
'normal', // コンテキスト
'default' // 表示優先度
);
}
add_action( 'add_meta_boxes', 'add_info_single_custom_field_meta_box' );
add_meta_box()のドキュメント
フィールドをレンダリング
input
タグを使って、編集画面のメタボックス内にフィールドをレンダリングする。この関数をコールバックとしてadd_meta_box()
に渡す。
nonceの説明は↓の記事がわかりやすかった。
// つづき
function render_info_single_custom_field_callback( $post ) {
$c = new InfoSingleCustomField();
$key = $c->key;
$nonce_name = $c->nonce_name;
$nonce_action = $c->nonce_action;
wp_nonce_field( $nonce_action, $nonce_name );
// trueを渡すと単一の値を取得する
$value = get_post_meta( $post->ID, $key, true );
?>
<div>
<label for="<?php echo( esc_attr( $key ) ); ?>">
テキスト
</label>
<input type="text"
id="<?php echo( esc_attr( $key ) ); ?>"
name="<?php echo( esc_attr( $key ) ); ?>"
value="<?php echo( esc_attr( $value ) ); ?>">
</div>
<?php
}
wp_nonce_field()
を実行して、メタデータと正しく紐づけるための隠れたinput要素を生成する。
wp_nonce_field( $nonce_action, $nonce_name )
label
タグのfor
属性とinput
タグのid
属性には、フィールドを識別するためのキーを指定する。
また、タグの属性に値を挿入する場合は、不正な文字列の挿入を防ぐためにesc_attr()
関数でエスケープする。
<div>
<label for="<?php echo( esc_attr( $key ) ); ?>">
テキスト
</label>
<input type="text"
id="<?php echo( esc_attr( $key ) ); ?>"
name="<?php echo( esc_attr( $key ) ); ?>"
value="<?php echo( esc_attr( $value ) ); ?>">
</div>
表示の確認
functions.php
でsingle-custom-field.php
を読み込む。
<?php
// 省略
require_once plugin_dir_path(__FILE__) . '/includes/info/single-custom-field.php';
ここまでの状態で、編集画面にメタボックスを表示できる。
ただし値の保存はまだできない。
データを保存
投稿の保存にフックしてメタデータの値を更新する。
// つづき
function save_info_single_custom_field_meta_box_data( $post_id ) {
$c = new InfoSingleCustomField();
$key = $c->key;
$post_type = $c->type;
$nonce_name = $c->nonce_name;
$nonce_action = $c->nonce_action;
// 投稿タイプをチェック
if ( get_post_type( $post_id ) !== $post_type ) {
return;
}
// ノンスチェック(ノンスが存在し、それが正しいか)
if ( !isset( $_POST[$nonce_name] ) || !wp_verify_nonce( $_POST[$nonce_name], $nonce_action ) ) {
return;
}
// 自動保存で更新を行わない
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return;
}
// 権限チェック
if ( !current_user_can( 'edit_post', $post_id ) ) {
return;
}
// データを取得
$new_value = isset( $_POST[$key] ) ? sanitize_text_field( $_POST[$key] ) : '';
// データを保存
update_post_meta( $post_id, $key, $new_value );
}
add_action( 'save_post', 'save_info_single_custom_field_meta_box_data' );
?>
$_POST[$key]
でnonceに入力された値を取得し、データ型が正しいことを保証するためにsanitize_text_field()
でサニタイズする。
$new_value = isset( $_POST[$key] ) ? sanitize_text_field( $_POST[$key] ) : '';
編集内容の保存をした時にメタデータを更新し保存する。
update_post_meta( $post_id, $key, $new_value );
ただし、↓の処理で編集の機能の自動保存時には更新しないようにする。
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return;
}
特に、'show_in_rest'にtrueを指定(ブロックエディタでの利用を有効化)してる時は自動保存機能がオンになるので注意。
投稿詳細ページで表示を確認
実際にフィールドに入力したテキストを投稿詳細ページに表示して確認してみる。
簡易的に詳細ページを作る。
<?php get_header(); ?>
<h1>info</h1>
<div>
<?php
global $post;
if ( !empty($post) ) :
?>
<div>
<h2><?php echo( $post->post_title ); ?></h2>
<small><?php echo( $post->post_date ); ?></small>
<div>
<small>
<?php
// メタデータの取得する
$post_meta = get_post_meta( $post->ID, 'info_single_custom_field', true );
echo( 'カスタムフィールド: ' . $post_meta );
?>
</small>
</div>
<div>
<?php print_r( $post->post_content ); ?>
</div>
</div>
<?php
endif;
?>
</div>
<?php get_footer(); ?>
'https://{domain}/wp/info/{slug}'にアクセスするとフィールドに入力した値が期待通りに保存・取得できているのがわかる。
プラグインとして実装(おまけ)
プラグインだとコントロールパネルで有効/無効を切り替えらるから便利かも。
wp/wp-contentにpluginsディレクトリを作ってinfoディレクトリをそのままぶちこむ。
ファイルの先頭に、wordpressがプラグインを識別するための記述をする。
<?php
/**
* Plugin Name: Custom Single Field
* Description: 特定の投稿タイプにカスタムフィールドを追加するオリジナルのプラグイン
*/
コントロールパネルの"プラグイン > インストール済みプラグイン"に↓のようにプラグインが追加されるので有効化すると使える。
あとがき
そのうち繰り返しフィールド編を投稿します。