プラグインを作りながら、WordPressのカスタマイズを学ぶシリーズ。
今回は、記事ごとにメモをつけるプラグインです。
プラグインでやりたいこと
投稿の備忘録を残すため、管理者のみが閲覧、編集できるメモを投稿に追加します。
- 投稿にメモ欄をつける
- 管理画面の記事一覧にメモを表示する
プラグインでは「Simple Post Notes」が有名ですね。
メモをどう保存するかがポイントですが、新たにデータベースのテーブルを作成しなくても、カスタムフィールドに項目を追加して対応するやり方でいけそうです。
データは通常、別の MySQL テーブルに保存されるので、新たに作る必要があります。しかし、まったく新しいテーブルに飛びつく前に、プラグインのデータを WordPress の投稿メタ (カスタムフィールド)へ保存してうまくいくか検討してみてください。投稿メタは望ましい方法ですので、可能であり実用的なら利用してください。
プラグインでデータベーステーブルを作る
カスタムフィールドは、投稿の「タイトル」や「本文」以外に、任意の項目を追加できる機能です。(カスタムフィールドは「メタデータ」とも呼ばれます。)カスタムフィールドを設定すると、データベースのwp_postmetaというテーブルに保存されます。
フィールド | 種別 | 備考 |
meta_id | bigint(20) unsigned | 主キー/(登録順に自動採番) |
post_id | bigint(20) unsigned | 必須/投稿ID |
meta_key | varchar(255) | カスタムフィールド のキー名 |
meta_value | longtext | カスタムフィールド の値 |
プラグインの作成手順
実際に以下の手順でプラグインを作成していきます。
- 投稿にメタボックス「メモ」を追加する
- 投稿の編集画面にカスタムフィールド「メモ」を表示する
- 投稿の編集画面で、カスタムフィールド「メモ」に入力された内容を保存する
- 投稿一覧にメモ欄を追加する
事前準備
コードに不備があると、WordPressのサイトが動かなくなってしまうので、開発環境でプラグインを作りましょう。
事前準備で、プラグインフォルダに、自作するプラグイン「my-plugin」を用意します。
plugins/
└ my-plugin/
└ my-plugin.php
今回はプラグインで作っていきますが、同じコードをfunctions.phpに記載する形でも作れます。
投稿にメタボックス「メモ」を追加する
記事の編集画面は「メタボックス」という枠で構成されています。
メタボックス内にカスタムフィールド「メモ」を用意します。
/**
* 投稿にメタボックス「メモ」を追加
*/
function add_memo_meta_box(){
// 投稿にメモ欄の追加
add_meta_box( 'post_memo','メモ', 'memo_meta_box_callback', 'post', 'side' );
}
add_action('admin_menu', 'add_memo_meta_box');
add_meta_box()関数で投稿と固定ページにpost_memoという名前のメタボックスを追加しています。
もし、固定ページにもメモ欄を追加する場合は、add_meta_box( ‘post_memo’,’メモ’, ‘memo_meta_box_callback’, ‘page’, ‘side’ );という行を追加します。(4つ目のパラメータ’post’が’page’に変わります。)
add_meta_box( $id, $title, $callback, $screen, $context, $priority, $callback_args );
投稿画面にメタボックスを追加する
$id: (必須) 編集画面セクションの HTML ID
$title: (必須) 編集画面セクションのタイトル、画面上に表示される
$callback: (必須) 編集画面セクションに HTML 出力する関数
$screen: カスタムフィールドを追加する投稿タイプ。
$context: カスタムフィールド追加する場所。(normal,advanced,sideのいずれか)
$priority: 表示される優先度。 (high, core, default または low)
$callback_args: コールバックに渡す引数。
admin_menuアクションフックは投稿編集画面が読み込まれた際に実行されます。
投稿の編集画面にカスタムフィールド「メモ」を表示する
編集画面に入力欄を表示するためのHTMLタグを記載します。
/**
* 投稿の編集画面にメタボックスを表示
* @param WP_Post $post
*/
function memo_meta_box_callback($post){
// nonceフィールドを追加して後でチェックする
wp_nonce_field( 'post_memo_meta_box_action', 'post_memo_meta_box_nonce' );
// 保存されているメモの値を取得
$value = get_post_meta($post->ID, 'post_memo', true);
// 入力フォームの出力
echo '<textarea id="post_memo" name="post_memo" placeholder="" rows="10" style="width: 100%;">'. esc_html($value) .'</textarea>';
}
関数の名前(post_memo_box)は、add_meta_boxの3つ目の引数callbackに指定した値です。
ここでは3つの処理をしています。
- セキュリティ担保のためnounceフィールドを追加
- 保存されているメモの値を取得
- テキストエリアの出力
nounceフィールドの追加
初めに、nonceフィールドを追加しています。
nonceはセキュリティを担保するためのトークンです。入力フォームから送信された値が、現在のサイトから送信されたものであると認証するために使われます。
nonce (ノンス)は、ある種の誤使用や悪意のある操作から URL やフォームを守るための「一度だけ使われる数値」です。WordPress の nonce は実際には数値ではなく、数字と英字で作られたハッシュです。また、本当に一度だけ使われるのではなく、無効になるまでの「有効期間」を持っています。
https://wpdocs.osdn.jp/WordPress_Nonce
(中略)デフォルトでは、nonce の有効期間は 1 日です。これを過ぎると、アクション文字列が一致しても nonce は無効です。
wp_nounce_filed()関数を使って、セキュリティトークンを発行します。
wp_nonce_field( $action, $name, $referer, $echo )
nonceを生成し、input要素のtype=”hidden”のフィールドを出力します。
$action : アクションの名前。この文字列を暗号化した値がvalue属性になる
$name nonce : フィールドの名前。作成されるinput要素のname 属性になる
$referer nounce : フィールドに加えて、リファラーを表す hidden フィールドを生成するかどうか。同一ページから送られてきたデータを確認するのに使う。
$echo : 出力か取得か。初期値は「true」で出力。
上記のコードによって出力されるHTML
<form method="post">
<input type="hidden" id="nonceフィールドの名前" name="nonceフィールドの名前" value="生成された値">
<input type="hidden" name="_wp_http_referer" value="/相対パス/">
</form>
入力フォームにhidden属性でnounceフィールドのinputタグが追加されます。入力フォームの内容を受け取って保存する際に、この値をチェックすることで、正当な操作であることを確認します。
カスタムフィールドの値の取得
続いて、保存されているメモの値を取得します。
// 保存されているメモの値を取得
$value = get_post_meta($post->ID, 'post_memo', true);
get_post_meta($post_id, $key, $single);
指定したIDの投稿のメタデータを取得する
$post_id(必須) カスタムフィールドを取得する投稿ID
$key 取得するカスタムフィールドのキー名。指定しない場合、全てのカスタムフィールドを取得する。
$single true をセットした場合、文字列を返す。falseまたは値をセットしなかった場合、関数はカスタムフィールドの配列を返す。
テキストエリアの出力
取得したメモの内容とともにテキストエリアを出力します。
// 入力フォームの出力
echo '<textarea id="post_memo" name="post_memo" placeholder="" rows="10" style="width: 100%;">'. esc_html($value) .'</textarea>';
投稿の編集画面で、カスタムフィールド「メモ」に入力された内容を保存する
/**
* 投稿が保存されたときに、カスタムフィールドの値も保存する
*
* @param int 投稿ID
* @return void
*/
function save_memo_meta_box( $post_id ) {
// nonceがセットされているかどうか確認
if ( ! isset( $_POST['post_memo_meta_box_nonce'] ) ) {
return;
}
// nonceが正しいかどうか検証
if ( ! wp_verify_nonce( $_POST['post_memo_meta_box_nonce'], 'post_memo_meta_box_action' ) ) {
return;
}
// 自動保存の場合はなにもしない
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return;
}
if (isset($_POST['post_memo'])) {
// 入力値をサニタイズ(無害化)
$post_memo = sanitize_text_field($_POST['post_memo']);
// カスタムフィールドを保存
update_post_meta( $post_id, 'post_memo', $post_memo );
}
add_action('save_post', 'save_memo_meta_box');
保存処理では3つの処理を行います。
- nounceのチェック
- カスタムフィールドに値を保存
- アクションフックにセット
nounceのチェック
wp_verify_nonce()関数で、nonceの値が正しいもので有効期限が切れていないことをチェックします。
// nonceがセットされているかどうか確認
if ( ! isset( $_POST['post_memo_meta_box_nonce'] ) ) {
return;
}
// nonceが正しいかどうか検証
if ( ! wp_verify_nonce( $_POST['post_memo_meta_box_nonce'], 'post_memo_meta_box_action' ) ) {
return;
}
wp_verify_nonce( $nonce, $action );
$nonce 検証する nonceの名前。
$action アクションの名前。nonce 生成時に設定した名前。
カスタムフィールドに値を保存
安全が確認できてから、メモの入力内容を保存します。
メモに入力値がセットされていた場合、入力内容を無害化(サニタイズ)して、保存します。
// 自動保存の場合はなにもしない
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return;
}
if (isset($_POST['post_memo'])) {
// 入力値をサニタイズ(無害化)
$post_memo = sanitize_text_field($_POST['post_memo']);
// カスタムフィールドを保存
update_post_meta( $post_id, 'post_memo', $post_memo );
}
update_post_meta( $post_id, $meta_key, $meta_value, $prev_value );
カスタムフィールド情報を更新する。指定した名前のカスタムフィールドがない場合は追加する。
$post_id : 投稿ID。
$meta_key : カスタムフィールド名。
$meta_value : カスタムフィールドの値。
$prev_value : 更新前のカスタムフィールドの値(省略時は”)。同じ名前のカスタムフィールドが複数登録されている場合は、この値によって区別する。
アクションフックにセット
add_action('save_post', 'save_memo_meta_box');
アクションフックsave_postは、投稿が下書き保存されたり、公開されたときに呼ばれます。
投稿一覧にメモ欄を追加する
/**
* 投稿一覧にメモカラムを追加
* @param columns
*/
function add_post_memo_columns($columns) {
$columns['post_memo'] = 'メモ';
return $columns;
}
add_filter( 'manage_posts_columns', 'add_post_memo_columns' );
/**
* メモカラムにカスタムフィールド「メモ」の内容を表示させる
* @param column_name カラム名
* @param post_id 投稿ID
*/
function add_post_memo_column($column_name, $post_id) {
if ( 'post_memo' == $column_name ) {
// メモの内容を取得
$value = get_post_meta($post_id, 'post_memo', true);
}
if ( isset($value)) {
echo attribute_escape($value);
}
}
add_action( 'manage_posts_custom_column', 'add_post_memo_column', 10, 2 );
フィルターフックmanage_posts_custom_columnで投稿一覧のカラムを追加します。
アクションフックmanage_posts_custom_column追加したカラムにメモの内容を設定します。
投稿を削除したとき、メモのデータを削除する
ここまででも問題なく動くのですが、メモを消すという処理がありません。空欄にして保存すると、空欄のままデータが残ります。なんとなく、wp_postmetaのデータがどんどん溜まっていくのが気になりました。
そこで、投稿をゴミ箱から完全に削除したときにメモの内容も削除するという処理を追加します。
本当はプラグインのアンインストール時にも消してあげた方がきれいなのかもしれません。
/**
* 投稿をゴミ箱から完全に削除した時、メモも削除する
* @param post_id
*/
add_action( 'before_delete_post', 'delete_post_memo' );
function delete_post_memo($post_id) {
delete_post_meta( $post_id, 'post_memo');
}
コードと動作確認
ここまでのプログラムをまとめるとこのようになります。
<?php
/**
* Add simple memo to posts.
*/
function add_memo_meta_box(){
// 投稿と固定ページにメモ欄の追加
add_meta_box( 'post_memo','メモ', 'memo_meta_box_callback', 'post', 'side' );
add_meta_box( 'post_memo','メモ', 'memo_meta_box_callback', 'page', 'side' );
}
add_action('admin_menu', 'add_memo_meta_box');
/**
* 投稿の編集画面にメタボックスを表示
* @param WP_Post $post
*/
function memo_meta_box_callback($post){
// nonceフィールドを追加して後でチェックする
wp_nonce_field( 'post_memo_meta_box_action', 'post_memo_meta_box_nonce' );
// 保存されているメモの値を取得
$value = get_post_meta($post->ID, 'post_memo', true);
// 入力フォームの出力
echo '<textarea id="post_memo" name="post_memo" placeholder="" rows="10" style="width: 100%;">'. esc_html($value) .'</textarea>';
}
/**
* 投稿が保存されたときに、カスタムフィールドの値も保存する
*
* @param int 投稿ID
* @return void
*/
function save_memo_meta_box( $post_id ) {
// nonceがセットされているかどうか確認
if ( ! isset( $_POST['post_memo_meta_box_nonce'] ) ) {
return;
}
// nonceが正しいかどうか検証
if ( ! wp_verify_nonce( $_POST['post_memo_meta_box_nonce'], 'post_memo_meta_box_action' ) ) {
return;
}
// 自動保存の場合はなにもしない
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return;
}
if (isset($_POST['post_memo'])) {
// 入力値をサニタイズ(無害化)
$post_memo = sanitize_text_field($_POST['post_memo']);
// カスタムフィールドを保存
update_post_meta( $post_id, 'post_memo', $post_memo );
}
}
add_action('save_post', 'save_memo_meta_box');
/**
* 投稿をゴミ箱から完全に削除した時、メモも削除する
* @param post_id
*/
add_action( 'before_delete_post', 'delete_post_memo' );
function delete_post_memo($post_id) {
delete_post_meta( $post_id, 'post_memo');
}
/**
* 投稿一覧にメモカラムを追加
* @param columns
*/
function add_post_memo_columns($columns) {
$columns['post_memo'] = 'メモ';
return $columns;
}
add_filter( 'manage_posts_columns', 'add_post_memo_columns' );
/**
* メモカラムにカスタムフィールド「メモ」の内容を表示させる
* @param column_name カラム名
* @param post_id 投稿ID
*/
function add_post_memo_column($column_name, $post_id) {
if ( 'post_memo' == $column_name ) {
// メモの内容を取得
$value = get_post_meta($post_id, 'post_memo', true);
}
if ( isset($value) ) {
echo attribute_escape($value);
}
}
add_action( 'manage_posts_custom_column', 'add_post_memo_column', 10, 2 );
編集画面には記事メモ欄が追加されています。
投稿の保存を押すと、メモの内容も保存されます。
wp_postmetaにはpost_memoのデータが入っています。
投稿一覧にはメモの内容が表示されています。
プラグインをクラス化する
類似プラグイン「Simple Post Note」のコードを読んでいると、プラグインをクラスとして書いていました。
プラグイン内の関数は他と重複してはいけません。
そのため、関数ベースで記載するときには、プラグイン接頭辞をつけるなどして名前が長くなるというデメリットがあります。
プラグインを一つのクラスに収容することで、その問題を解決できます。
詳しくはこちらの本に解説されていました↓
- 他のプラグインやテーマとのメソッド名が競合せずにすむ
- メソッド名が短くわかりやすくなる
- 再利用しやすくなる
今回のコードをクラス化したコードにしました。
<?php
/**
* Add simple memo to posts.
*/
class Simple_Memo{
/**
* Class constructor
*/
public function __construct() {
add_action( 'admin_menu', array($this,'add_meta_box'));
add_action( 'before_delete_post', array($this,'delete_post_memo'));
add_action( 'save_post', array($this,'save_meta_box'));
add_filter( 'manage_posts_columns', array($this, 'add_column') );
add_action( 'manage_posts_custom_column', array($this, 'output_column'), 10, 2 );
}
public function add_meta_box(){
// 投稿と固定ページにメモ欄の追加
add_meta_box( 'post_memo','メモ', array( $this, 'meta_box_callback'), 'post', 'side' );
}
/**
* 投稿の編集画面にメタボックスを表示
* @param WP_Post $post
*/
public function meta_box_callback($post){
// nonceフィールドを追加して後でチェックする
wp_nonce_field( 'post_memo_meta_box_action', 'post_memo_meta_box_nonce' );
// 保存されているメモの値を取得
$value = get_post_meta($post->ID, 'post_memo', true);
// 入力フォームの出力
echo '<textarea id="post_memo" name="post_memo" placeholder="" rows="10" style="width: 100%;">'. esc_html($value) .'</textarea>';
}
/**
* 投稿が保存されたときに、カスタムフィールドの値も保存する
*
* @param int 投稿ID
* @return void
*/
public function save_meta_box( $post_id ) {
// nonceがセットされているかどうか確認
if ( ! isset( $_POST['post_memo_meta_box_nonce'] ) ) {
return;
}
// nonceが正しいかどうか検証
if ( ! wp_verify_nonce( $_POST['post_memo_meta_box_nonce'], 'post_memo_meta_box_action' ) ) {
return;
}
// 自動保存の場合はなにもしない
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return;
}
if (isset($_POST['post_memo'])) {
// 入力値をサニタイズ(無害化)
$post_memo = sanitize_text_field($_POST['post_memo']);
// カスタムフィールドを保存
update_post_meta( $post_id, 'post_memo', $post_memo );
}
}
/**
* 投稿をゴミ箱から完全に削除した時、メモも削除する
* @param post_id
*/
public function delete_post_memo($post_id) {
delete_post_meta( $post_id, 'post_memo');
}
/**
* 投稿一覧にメモカラムを追加
* @param columns
*/
public function add_column($columns) {
$columns['post_memo'] = 'メモ';
return $columns;
}
/**
* メモカラムにカスタムフィールド「メモ」の内容を表示させる
* @param column_name カラム名
* @param post_id 投稿ID
*/
public function output_column($column_name, $post_id) {
if ( 'post_memo' == $column_name ) {
// メモの内容を取得
$value = get_post_meta($post_id, 'post_memo', true);
}
if ( isset($value) ) {
echo attribute_escape($value);
}
}
}
new Simple_Memo();
- フックへの登録はコンストラクタ内で行う
- フックへするときは
add_action( 'admin_menu', array($this,'add_meta_box'));
のように記述する。 - メソッド名は処理内容に合わせて短くできる