カスタムフィールドはプラグインなしでも扱えます。読み出しはget_post_meta()の1行です。
<?php $price = get_post_meta(get_the_ID(), 'price', true); ?>
<?php if ($price !== '') : ?>
<p class="price">¥<?php echo esc_html(number_format((int) $price)); ?></p>
<?php endif; ?>第3引数のtrueが最初の関門です。ここを理解すれば、あとは応用だけです。
第3引数(single)の意味
投稿メタは同じキーに複数の値を持てる仕組みなので、返し方が2通りあります。
get_post_meta(get_the_ID(), 'price', true); // '3300'(値そのもの)
get_post_meta(get_the_ID(), 'price', false); // ['3300'](配列)
get_post_meta(get_the_ID(), 'price'); // 省略時もfalseと同じ=配列ふつうは1つの値を使うのでtrueを付けます。trueを忘れるとArrayと表示される、というのが定番のつまずきです。
キーも省略すると、その投稿のメタ全部が配列で返ります。デバッグに便利です。
<?php var_dump(get_post_meta(get_the_ID())); ?>入力欄を出す(標準のカスタムフィールド欄)
ブロックエディタでは、標準のカスタムフィールド欄が最初は隠れています。
- 編集画面の右上「⋮」→「設定」を開く
- 「パネル」の中の「カスタムフィールド」をオンにする(画面が再読みこみされます)
- 編集画面の下に「カスタムフィールド」欄が出る
- 「新規追加」で名前
price・値3300を入力して追加
カスタム投稿タイプでこの欄を使うには、register_post_type()のsupportsに'custom-fields'を含めます。
保存・更新・削除
プログラムから値を書きこむときはこの3つです。
update_post_meta($post_id, 'price', 3300); // 無ければ追加、あれば更新(基本これ)
add_post_meta($post_id, 'tag', 'sale'); // 同じキーに値を足す(複数値になる)
delete_post_meta($post_id, 'price'); // 削除迷ったらupdate_post_meta()。「あれば更新、無ければ追加」なので、二重登録の事故が起きません。
数値として使うときの注意
投稿メタの値は、種類を区別せず文字列として保存されています。計算や比較に使うときは型を決めます。
<?php $price = (int) get_post_meta(get_the_ID(), 'price', true); ?>
<p>税込 <?php echo esc_html(number_format((int) round($price * 1.1))); ?>円</p>一覧を価格で絞る・並べ替えるときも、同じ理由で型の指定が必要です。
べんりワザmeta_queryでカスタムフィールドで絞る・並べ替えるmeta_queryで絞りこむ・並べ替える書き方はこちら「0」を表示したいときの落とし穴
<?php if (get_post_meta(get_the_ID(), 'stock', true)) : ?> <!-- ❌ 0が「無い」扱いになる -->
<?php if (get_post_meta(get_the_ID(), 'stock', true) !== '') : ?> <!-- ✅ -->在庫0・価格0を表示したいサイトでは、空文字と比較する書き方にします。PHPでは0も'0'も条件式では偽になるためです。
アンダースコアで始まるキー
_で始まるメタキー(_thumbnail_idなど)は保護されたメタとして扱われ、標準のカスタムフィールド欄には出てきません。
- 画面から編集させたい値→
priceのように普通の名前 - プログラム専用の内部値→
_my_internal_flagのようにアンダースコア始まり
WordPressやプラグインが使う内部データも、この形で保存されています。
ブロックエディタやREST APIで使うなら登録する
ブロックエディタのサイドバーやREST API経由でメタを読み書きしたい場合は、register_post_meta()で宣言しておきます。
add_action('init', function () {
register_post_meta('shohin', 'price', [
'type' => 'number',
'single' => true,
'show_in_rest' => true,
'auth_callback' => function () {
return current_user_can('edit_posts');
},
]);
});show_in_restを立てることで、ブロックエディタ(JavaScript側)からも扱える値になります。
ACF(SCF)との関係
ACFも内部では同じ投稿メタとして保存しているので、get_post_meta()で読めます。ただし違いがあります。
get_post_meta() | get_field()(ACF/SCF) | |
|---|---|---|
| 返るもの | 保存されている生の値 | 型に応じて整形された値 |
| 画像フィールド | IDの文字列 | 設定に応じてID・URL・配列 |
| くり返し・グループ | 行数や内部キーの生データ | 使いやすい配列 |
ACFを入れているならget_field()を使うのが確実です。get_post_meta()が活躍するのは、プラグインを入れない構成や、プラグインが停止しても壊れないテーマを作るときです。
入力欄を自作する(add_meta_box)
標準の「名前と値」の欄は、運用する人には分かりづらいものです。専用の入力欄を作るならadd_meta_box()を使います。
add_action('add_meta_boxes', function () {
add_meta_box('shohin_price', '価格', function ($post) {
$value = get_post_meta($post->ID, 'price', true);
wp_nonce_field('shohin_price_save', 'shohin_price_nonce');
echo '<input type="number" name="price" value="' . esc_attr($value) . '">';
}, 'shohin', 'side');
});
add_action('save_post_shohin', function ($post_id) {
if (!isset($_POST['shohin_price_nonce'])
|| !wp_verify_nonce($_POST['shohin_price_nonce'], 'shohin_price_save')) {
return;
}
if (!current_user_can('edit_post', $post_id)) {
return;
}
update_post_meta($post_id, 'price', (int) ($_POST['price'] ?? 0));
});保存処理ではnonceの確認と権限の確認が必須です。ここを省くと、外部から値を書きこめる穴になります。
べんりワザエスケープ関数の使い分け(esc_html・esc_attr・esc_url)エスケープとnonceの使い分けはこちらまとめ
- 読み出しは
get_post_meta($id, 'キー', true)。trueが「1つの値として取る」指定 - 書きこみは
update_post_meta()が基本(無ければ追加・あれば更新) - 値は文字列で保存されるので、数値は
(int)で型を決める。0の表示は空文字と比較する - ACFを入れているなら
get_field()が確実。標準機能はプラグイン非依存のテーマ向き
標準機能で扱えると分かると、「プラグインが無いから作れない」場面がなくなります。