閉じる

技術ブログ

【WordPress】カスタムフィールドで記事の並び順を変える ── テキスト・日付(カレンダー)フィールド徹底整理

2026.09.16

WordPress 実践シリーズ #08
カスタムフィールドで記事の並び順を変える
── テキスト・カレンダー(日付)編
「テキストやカレンダーなどのフィールドを利用して、記事の並び順を変更するには、どのような方法がありますか?」──
meta_value / meta_type / pre_get_posts を、公式ドキュメントとコア実装で裏取りしながら整理する

WordPressの投稿一覧は、標準では投稿日の新しい順に並びます。しかし実務では「イベント開催日順に並べたい」「表示順の数字で手動に並べたい」「五十音順に並べたい」という要望が必ず出てきます。

このとき使うのがカスタムフィールド(post meta)による並べ替えです。やること自体は WP_Query に2〜3行足すだけ。ところが現場では、「並ばない」「一部の記事が消える」「日付なのに順番がおかしい」の3つの事故が驚くほどよく起きます。

本記事では、テキスト型・日付(カレンダー)型それぞれの正しい書き方と、なぜ事故が起きるのかを仕組みから説明します。記載内容は WordPress 公式リファレンスと、当ブログが稼働している WordPress 7.1 のコア実装(class-wp-query.php)および実データベースの索引で裏を取っています。

1. 結論(早見表)

先に答えだけ置きます。「並べ替えの鍵(meta_key)を指定し、orderby で meta_value 系を選ぶ」——これがすべての基本です。

やりたいこと 書き方(WP_Query の引数)
テキスト(文字)で並べる 'meta_key' => 'キー名''orderby' => 'meta_value'
数値(表示順・価格など)で並べる 'meta_key' => 'キー名''orderby' => 'meta_value_num'
カレンダー(日付)で並べる 'meta_key' => 'キー名''orderby' => 'meta_value'(保存形式が桁揃いなら、これだけで時系列に並ぶ)
日付として厳密に扱いたい 上記に 'meta_type' => 'DATE'(または 'DATETIME')を追加
フィールドが空の記事も残したい meta_query'relation' => 'OR''compare' => 'NOT EXISTS'(→ 5章)
2つのフィールドで並べたい 名前付き meta_query 句を作り、orderby に句の名前を書く(→ 3章)
アーカイブなど一覧全体に効かせたい テンプレートではなく pre_get_posts で本クエリを書き換える(→ 6章)
🔴 先に警告:'orderby' => 'meta_value_datetime' という指定は存在しません。
ネット記事やAIの回答で見かけますが、WordPressコアに実装がありません。指定してもエラーにはならず、黙って無視されて投稿日順に戻ります。実測の根拠は4章に書きました。

2. 仕組み:なぜ「ひと手間」必要なのか

並べ替えが素直に書けない理由は、データの置き場所にあります。

テーブル 入っているもの 並べ替え
wp_posts タイトル・本文・投稿日・menu_order など投稿そのものの列 そのまま ORDER BY できる('orderby' => 'date' 等)
wp_postmeta カスタムフィールド。post_id / meta_key / meta_value縦持ち 投稿テーブルとJOINしてからでないと並べられない

つまりカスタムフィールドでの並べ替えは、内部的にはこういうSQLが組み立てられています。

— ‘meta_key’ => ‘event_date’, ‘orderby’ => ‘meta_value’ が生成するSQL(要約)
SELECT wp_posts.* FROM wp_posts
INNER JOIN wp_postmeta AS mt1 ON ( wp_posts.ID = mt1.post_id )
WHERE post_type = ‘post’ AND post_status = ‘publish’
AND mt1.meta_key = ‘event_date’
ORDER BY mt1.meta_value ASC

このSQLの形から、現場で起きる事故の理由がそのまま読み取れます。

このSQLが教えてくれる2つのこと
INNER JOIN である → そのフィールドを持っていない記事は結果から消える(→ 5章)
meta_value の型は longtext文字列 → 数値も日付も「文字として」並ぶ(→ 3章・4章)

3. テキストフィールドで並べる

3-1. 基本形

// 例:カスタムフィールド ‘company_kana’(ふりがな)で五十音順に並べる
$args = [
‘post_type’ => ‘post’,
‘posts_per_page’ => 20,
‘meta_key’ => ‘company_kana’, // ← 並べ替えの鍵。これが無いと効かない
‘orderby’ => ‘meta_value’, // ← 文字列として並べる
‘order’ => ‘ASC’, // ASC=昇順 / DESC=降順
];
$query = new WP_Query( $args );
公式リファレンスにも meta_value を使うには meta_key=keyname がクエリに存在していなければならない」 と明記されています。「orderbyだけ書いたのに並ばない」の原因は、ほぼこれです。

3-2. 数字を入れたテキストは「文字の順」で並ぶ

「表示順」のような数値をフィールドに入れている場合、meta_value のままだと辞書順になります。

// meta_value(文字列)で並べた場合 ← 想定外の並び
1, 10, 11, 2, 20, 3, 4

// meta_value_num(数値)で並べた場合 ← 期待どおり
1, 2, 3, 4, 10, 11, 20

数値で並べたいときは 'orderby' => 'meta_value_num'(WordPress 2.8以降)を使います。コア内部では meta_value+0 という形で数値化されます。

3-3. 日本語を五十音順に並べたいとき

漢字のフィールドをそのまま並べても五十音順にはなりません。並び順はデータベースの照合順序(文字コード順)に従うため、「読み」ではなく「文字コード」で並びます。
実務の定石は「ふりがな専用フィールドを別に持ち、そちらで並べる」です(例:companycompany_kana の2つを用意し、meta_key には company_kana を指定する)。

3-4. 複数の条件で並べる

WordPress 4.0以降、orderby配列を渡して「第1キー・第2キー」を指定できます。

// 表示順(数値)の降順 → 同点ならタイトル昇順
$args = [
‘meta_key’ => ‘priority’,
‘orderby’ => [ ‘meta_value_num’ => ‘DESC’, ‘title’ => ‘ASC’ ],
];

2つの異なるカスタムフィールドで並べたい場合(例:都道府県 → 市区町村)は、meta_query名前を付けた句を作り、その名前を orderby に書きます。

// 名前付き meta_query 句を orderby から参照する(公式リファレンス掲載の書き方)
$q = new WP_Query( [
‘meta_query’ => [
‘relation’ => ‘AND’,
‘pref_clause’ => [ ‘key’ => ‘pref’, ‘compare’ => ‘EXISTS’ ],
‘city_clause’ => [ ‘key’ => ‘city’, ‘compare’ => ‘EXISTS’ ],
],
‘orderby’ => [
‘pref_clause’ => ‘ASC’, // ← 句の名前をそのまま書ける
‘city_clause’ => ‘ASC’,
],
] );

4. カレンダー(日付)フィールドで並べる ★本題

4-1. まず「どんな文字列で保存されているか」を知る

日付フィールドの並べ替えは、保存形式がすべてです。meta_value は文字列なので、「左から桁が揃っている形式」であれば文字列のまま並べても正しい時系列になります

入力方法 データベースに入る形 文字列のまま並ぶ?
ACF / SCF デートピッカー Ymd(表示形式や返り値形式の設定に関わらず固定) 20260916 並ぶ
ACF / SCF デート&タイムピッカー Y-m-d H:i:s 2026-09-16 14:00:00 並ぶ
HTML標準の <input type="date"> Y-m-d 2026-09-16 並ぶ
自由入力のテキスト欄 入力者しだい(ゼロ埋めなし・和暦混在) 2026/9/16令和8年9月16日 並ばない
ACFのデートピッカーは Ymd(例 20260916)で保存されます。これは「年→月→日」の順に桁が固定されているため、文字列として並べても、数値として並べても、結果は同じ時系列になります。
つまり 'meta_key' => 'event_date''orderby' => 'meta_value' の2行だけで、カレンダー順の一覧が作れます。
// イベント開催日(ACFデートピッカー)の近い順に並べる
$args = [
‘post_type’ => ‘event’,
‘posts_per_page’ => 10,
‘meta_key’ => ‘event_date’,
‘orderby’ => ‘meta_value’,
‘order’ => ‘ASC’,
];
🔴 危ないのは「自由入力のテキスト欄に日付を書かせている」パターンです。
2026/9/162026/10/1 を文字列で並べると、2026/10/1 のほうが先に来ます(1 < 9 のため)。日付を扱うなら必ず日付専用フィールドを使うか、保存形式をゼロ埋めで固定してください。

4-2. 「今日以降の予定だけ」を日付順に出す

実務でいちばん多い要件です。並べ替えと絞り込みを同時に行います。

// 本日以降のイベントを、開催日の近い順に
$today = date_i18n( ‘Ymd’ ); // ACFデートピッカーと同じ形式に揃えるのがコツ

$args = [
‘post_type’ => ‘event’,
‘meta_key’ => ‘event_date’,
‘orderby’ => ‘meta_value’,
‘order’ => ‘ASC’,
‘meta_query’ => [
[
‘key’ => ‘event_date’,
‘value’ => $today,
‘compare’ => ‘>=’,
‘type’ => ‘NUMERIC’, // Ymd(8桁)なら数値比較が確実
],
],
];

比較の型を合わせるのが要点です。保存が Ymd20260916)なら 'type' => 'NUMERIC'、保存が Y-m-d なら 'type' => 'DATE' と、保存形式と比較の型を必ず揃えます。比較値($today)も同じ形式で作ります。

4-3. meta_type を付けるとどうなるか

形式が揃っていない場合や、日付として厳密に扱いたい場合は meta_type を指定します。

$args = [
‘meta_key’ => ‘start_at’,
‘meta_type’ => ‘DATETIME’, // NUMERIC / BINARY / CHAR / DATE / DATETIME / DECIMAL / SIGNED / TIME / UNSIGNED
‘orderby’ => ‘meta_value’, // ← meta_value のまま。専用の値は要らない
‘order’ => ‘ASC’,
];

このとき、コアは ORDER BY mt1.meta_value ではなく ORDER BY CAST(mt1.meta_value AS DATETIME) というSQLを組み立てます(class-wp-query.phpparse_orderby())。

4-4. 🔴 実測:meta_value_datetime は存在しない

日付の並べ替えでもっとも多い誤りがこれです。'orderby' => 'meta_value_datetime''meta_value_date' はWordPressコアに実装がありません。

当ブログが稼働する WordPress 7.1 の wp-includes/class-wp-query.php を実際に検索しても、meta_value_datetime という文字列は1件も出てきません。コアが orderby として受け付けるのは、次のものだけです。

// class-wp-query.php parse_orderby() が許可する「メタ関連」のキー(WordPress 7.1 実装)
‘meta_value’ // meta_type があれば CAST される
‘meta_value_num’ // meta_value+0(数値)
$primary_meta_key // meta_key に指定したキー名そのもの
名前付き meta_query 句のキー名 // ‘city_clause’ など

// ↑ここに無い値を渡すと parse_orderby() は false を返し、
// その指定は捨てられて既定(投稿日順)に戻る=エラーは出ない

症状は「エラーも警告も出ないのに、並び順が変わらない」。この静かな失敗が、原因追跡をいちばん難しくします。日付で並べたいときは 'meta_type' => 'DATE'(または 'DATETIME')+ 'orderby' => 'meta_value' と書いてください。
(この挙動は WordPress の課題管理システムでも Ticket #50081 として報告されています)

5. 最大の落とし穴:フィールドが空の記事が消える

2章で見たとおり、meta_key を指定した並べ替えは INNER JOIN です。つまり——

そのカスタムフィールドを持っていない(=一度も保存していない)記事は、一覧から丸ごと消えます。
「並べ替えを入れたら記事が20件から12件に減った」という相談の原因は、ほぼ100%これです。新しく追加したフィールドは、既存の古い記事には存在しないためです。

5-1. 空の記事も残す書き方

// 日付があるものは日付順、無いものも一覧から消さない
$args = [
‘post_type’ => ‘event’,
‘meta_query’ => [
‘relation’ => ‘OR’,
‘has_date’ => [ ‘key’ => ‘event_date’, ‘compare’ => ‘EXISTS’ ],
‘no_date’ => [ ‘key’ => ‘event_date’, ‘compare’ => ‘NOT EXISTS’ ],
],
‘orderby’ => [ ‘has_date’ => ‘ASC’ ], // ← 名前付き句を指定(meta_key は書かない)
];

ポイントは2つです。

ポイント 理由
relation => 'OR'NOT EXISTS を並べる 「フィールドがある記事」と「無い記事」の両方を対象に含めるため(NOT EXISTS は WordPress 3.9以降、value の指定が不要)
orderby には句の名前を書く meta_keymeta_value 方式だと、OR条件を書いても並べ替え側で結局JOINが効いてしまうため
ただし NOT EXISTS はサブクエリを生みます。記事数が多いサイトでは負荷になり得ます。
根本的には「保存時に既定値を入れておく」ほうが健全です(例:save_post で日付が空なら投稿日を入れる/一括投入スクリプトで既存記事に初期値を付ける)。データを揃えてしまえば、クエリは単純なままで済みます。

6. 一覧ページ全体・管理画面に効かせる(pre_get_posts)

6-1. アーカイブや検索結果の並び順を変える

ここまでの new WP_Query() は「そのテンプレート内の独自ループ」の話です。アーカイブページそのものarchive-event.php など)の並び順を変えるときは、テンプレートで query_posts() を使うのではなく、pre_get_posts で本クエリを書き換えます。

// functions.php
add_action( ‘pre_get_posts’, function ( $query ) {

// 管理画面には効かせない/本クエリだけに効かせる(この2行が必須)
if ( is_admin() || ! $query->is_main_query() ) {
return;
}

if ( $query->is_post_type_archive( ‘event’ ) ) {
$query->set( ‘meta_key’, ‘event_date’ );
$query->set( ‘orderby’, ‘meta_value’ );
$query->set( ‘order’, ‘ASC’ );
}
} );

is_admin()is_main_query() のガードを忘れると、管理画面の投稿一覧やサイト内の全サブループまで巻き添えで並び替わります。「管理画面から記事が消えた」という事故の典型パターンです。

6-2. 管理画面の一覧を「列クリックで並べ替え」可能にする

編集画面側で日付順に並べたい、という要望もよくあります。列の見出しをクリックできるようにするには、2つのフックを組み合わせます。

// ① 「開催日」列を並べ替え可能にする(event は投稿タイプ名)
add_filter( ‘manage_edit-event_sortable_columns’, function ( $columns ) {
$columns[‘event_date’] = ‘event_date’; // 値はクリック時に orderby として渡される
return $columns;
} );

// ② クリックされたら実際の並べ替えを組み立てる
add_action( ‘pre_get_posts’, function ( $query ) {
if ( ! is_admin() || ! $query->is_main_query() ) {
return;
}
if ( ‘event_date’ === $query->get( ‘orderby’ ) ) {
$query->set( ‘meta_key’, ‘event_date’ );
$query->set( ‘orderby’, ‘meta_value’ );
}
} );

7. コードを書かない選択肢(プラグイン・ブロックテーマ)

方法 向いている場面 注意点
menu_order(手動並べ替え)
ドラッグ&ドロップ系プラグイン
「担当者が見た目で自由に並べたい」。日付や数値のルールが無い場合 記事が増えると運用が破綻しやすい。'orderby' => 'menu_order' で取り出す
ACF / SCF などのフィールド系 入力UIを作る用途。日付ピッカーやカレンダー入力はここ 並べ替え自体はしてくれない。結局 WP_Query 側の指定が必要
Meta Box のカスタムテーブル 件数が多く、wp_postmeta の負荷が問題になる案件 専用テーブルに保存されるため、meta_key 方式の標準クエリとは書き方が変わる
ブロックテーマのクエリループ ブロックエディタだけでページを組みたい場合 管理画面のUIで選べる並び順は「新しい順/古い順/A→Z/Z→A」程度。カスタムフィールド順は標準UIでは選べない

クエリループブロックでカスタムフィールド順にしたい場合は、query_loop_block_query_vars フィルター(WordPress 6.1以降)でクエリ引数を差し替えます。

// クエリループブロックの並び順をカスタムフィールド順に差し替える
add_filter( ‘query_loop_block_query_vars’, function ( $query, $block, $page ) {
if ( ! empty( $query[‘post_type’] ) && ‘event’ === $query[‘post_type’] ) {
$query[‘meta_key’] = ‘event_date’;
$query[‘orderby’] = ‘meta_value’;
$query[‘order’] = ‘ASC’;
}
return $query;
}, 10, 3 );

8. パフォーマンスと運用の注意

8-1. 実測:wp_postmeta には meta_value の索引が無い

当ブログのデータベースで wp_postmeta の索引を確認すると、存在するのは次の3つだけでした(WordPress 標準スキーマどおり)。

SHOW INDEX FROM wp_postmeta;
PRIMARY meta_id
post_id post_id
meta_key meta_key (先頭191文字)

— meta_value には索引が無い → ORDER BY meta_value はソート処理(filesort)になる

カスタムフィールドでの並べ替えは、件数が増えるほど確実に重くなります。数十〜数百件なら問題になりませんが、数千件規模で毎回全件を並べ替えるのは避けてください。
対策:posts_per_page で件数を絞る/キャッシュを併用する/並べ替え専用の値は menu_order や投稿日(post_date)など投稿テーブル側の列に寄せる/大規模ならカスタムテーブルを検討する。

8-2. 運用で決めておくべきこと

決めること 理由
保存形式を先に固定する(ゼロ埋め・固定長) 途中で形式を変えると、過去データだけ並ばなくなる。後からの一括変換は手間が大きい
フィールドを空にできるか決める 空を許すなら5章の対応が必須。許さないなら必須入力にするか既定値を入れる
「表示順」を人が触るか決める 人が触るなら menu_order+並べ替えプラグイン、ルールで決まるならカスタムフィールドが向く

9. まとめ(ご質問への回答)

Q1. テキストフィールドを使って記事の並び順を変えるには?
'meta_key' => 'キー名''orderby' => 'meta_value' をセットで指定します。数値を入れているフィールドなら 'meta_value_num' に変えてください(文字列のままだと 1, 10, 2 の順になります)。日本語を五十音順にしたいときは、ふりがな専用フィールドを別に用意してそちらで並べるのが確実です。
Q2. カレンダー(日付)フィールドではどう書く?
同じく 'meta_key''orderby' => 'meta_value' です。ACFのデートピッカーは Ymd(例 20260916)という桁の揃った形式で保存されるため、文字列のまま並べても正しく時系列になります。厳密に日付として扱いたい場合だけ 'meta_type' => 'DATE'(時刻付きなら 'DATETIME')を足します。'meta_value_datetime' という指定は存在しないので使わないでください。
Q3. いちばん気を付けることは?
3つあります。
そのフィールドが無い記事は一覧から消えるINNER JOIN のため)。残したいなら NOT EXISTS を併用するか、既定値を入れて回る。
失敗しても黙って投稿日順に戻る。並ばないときは、まず meta_key の綴りと orderby の値が正しいかを疑う。
アーカイブページ全体に効かせるなら pre_get_posts。その際 is_admin()is_main_query() のガードを必ず入れる。

参考(一次ソース)

※本記事の動作・数値は、WordPress 7.1 環境(tech.jnome.jp)の実装およびデータベースで確認しています。

作成: Seeds Brains(ジェイノーム業務支援AI)