Back to list
JetSmartFilters と Bricks Builder を使ってカスタムフィールド値による投稿のフィルタリングとソート方法
How to Filter and Sort Posts by Custom Field Value Using JetSmartFilters + Bricks Builder
Translated: 2026/4/25 3:00:17
Japanese Translation
ユーザーが数値カスタムフィールド(製品仕様に例)による投稿リストのフィルタリングとソートを試みたことがあるなら、おそらく JetSmartFilters (JSF) と Bricks Builder がデフォルトで完全に連携していない壁に遭遇したことがあります。
この記事では、その接続方法を完全に記述します:ラジオフィルタをクリックすると、当該フィールド値を持たない投稿のみを除き、残りの結果をその数値値を降順にソートする処理を行うものです。
カスタムフィールド名(例:"Weight"、"Length"、"Rating")のサイドバーラジオリストがあります。ユーザーがいずれかを選択すると、以下の動作が行われます:
- そのフィールド値を持つ投稿のみが表示されます。
- 結果は、そのフィールドの数値値を降順にソートされます。
- すべての他のアクティブなフィルタ(分類、検索など)が正しくスタックされ続けます。
実装要件:
- Bricks Builder — カスタム Query ID を持つクエリループ
- JetEngine — カスタムメタフィールド(すべて数値)を持つカスタム投稿タイプ (CPT)
- JetSmartFilters — 同一ページのラジオフィルタと既存の分類フィルタ
JSF はソートウィジェットとラジオフィルタウィジェットを提供しますが、それらは独立して動作します。ラジオをクリックしてもソートがトリガーされず、「このラジオ値が選択されたときに、そのメタキーによってソートする」といったネイティブな指定もできません。
解決策として、JSF のラジオフィルタを使って単純なクエリ変数を渡にし、Bricks のクエリを bricks/posts/query_vars フィルターフックを通じて取り込み、正しい meta_query, orderby, meta_key をすべてサーバーサイドで注入します(JavaScript 不要)。
1. JetSmartFilters 設定:
- 新しいラジオフィルタを作成し、以下の設定を使用:
- データソース:手動入力
- オプションリスト:各カスタムフィールドにつき 1 項目。ラベル = 表示名、値 = メタキー。
ラベル:重量 値:weight
ラベル:長さ 値:length
ラベル:評価 値:rating
- 対象:Bricks クエリループ
- クエリ ID:your-listing-id(Bricks ループ要素に設定した Query ID と一致させること)
- 適用タイミング:値変更時
- クエリ変数:_plain_query::sort_by_field
_plain_query::プレフィックスは重要です。JSF を単純なクエリ変数として値を渡させ、その後の独自メタクエリ処理を避けさせるためです。JSF は内部でこれを POST パイロードの _plain_query_sort_by_field へ変換します。
2. Bricks Builder 設定:
- クエリループ要素を選択し、JSF パネル下の Query ID を設定します(要素がフィルタブルとマーク付けられた際に追加された「Query ID」とラベルされたフィールド)。
- 例:my-listing
- ページ上のすべての JSF フィルタウィジェットは、すべて「このフィルタを対象と」設定に同じ Query ID を設定する必要があります。
3. サーバーサイドスクリプト実装:
テーマの functions.php またはサイト固有プラグインに追加。
add_filter('bricks/posts/query_vars', function($query_vars, $settings, $element_id, $element_name) {
// 特定の Bricks ループを JSF Query ID でターゲットにする
// Bricks はこれを'jsfb_query_id'に格納し、'query_id'ではありません
$query_id = $settings['jsfb_query_id'] ?? '';
if ($query_id !== 'my-listing') return $query_vars;
// JSF は AJAX リクエスト中に POST 経由でフィルタデータを送ります
// _plain_query::プレフィックスは POST キーの _plain_query_ へ変わる
$query_data = $_POST['query'] ?? [];
$sort_field = sanitize_key($query_data['_plain_query_sort_by_field'] ?? '');
if (empty($sort_field)) return $query_vars;
// ユーザー入力に直接信頼しないため、許可されたフィールドキーのホワイトリストを使用
$allowed_fields = ['weight', 'length', 'rating']; // 全フィールドをここに入れる
if (!in_array($sort_field, $allowed_fields, true)) return $query_vars;
// フィルタ:このフィールド値を持つ非空投稿のみを表示
$query_vars['meta_query'] = [
'relation' => 'AND',
[
'key' => $sort_field,
'compare' => 'EXISTS',
],
[
'key' => $sort_field,
'value' => '',
'compare' => '!=',
],
];
// ソート:選択されたフィールドの数値値を降順にソート
$query_vars['orderby'] = 'meta_value_num';
$query_vars['order'] = 'DESC';
$query_vars['meta_key'] = $sort_field;
};
Original Content
How to Filter and Sort Posts by Custom Field Value Using JetSmartFilters + Bricks Builder
If you've ever tried to let users filter and sort a post listing by a numeric custom field — say, a product spec, a rating, or a measurement — you've probably hit the wall where JetSmartFilters (JSF) and Bricks Builder don't quite connect out of the box.
This post documents exactly how to wire them together: a radio filter that, when clicked, filters out posts missing that field and sorts the remaining results by that field's numeric value in descending order.
A sidebar radio list of custom field names (e.g. "Weight", "Length", "Rating"). When a user selects one:
Only posts that have a value for that field are shown
Results are sorted highest to lowest by that field's numeric value
All other active filters (taxonomy, search, etc.) continue to stack correctly
Bricks Builder — query loop with a custom Query ID
JetEngine — CPT with custom meta fields (all numeric)
JetSmartFilters — Radio filter + existing taxonomy filters on the same page
JSF has a Sort widget and a Radio filter widget, but they operate independently. Clicking a radio does not trigger a sort. You can't natively say "when this radio value is selected, sort by that meta key."
The solution is to use JSF's radio filter to pass a plain query variable, then intercept Bricks' query via the bricks/posts/query_vars filter hook to inject the correct meta_query, orderby, and meta_key — all server-side, no JavaScript needed.
In JetSmartFilters, create a new Radio filter with the following settings:
Data Source: Manual Input
Options List: One entry per custom field. Label = display name, Value = the meta key
Label: Weight Value: weight
Label: Length Value: length
Label: Rating Value: rating
This filter for: Bricks query loop
Query ID: your-listing-id (must match the Query ID set on your Bricks loop element)
Apply on: Value change
Query Variable: _plain_query::sort_by_field
The _plain_query:: prefix is important. It tells JSF to pass the value as a plain query variable rather than attempting its own meta query processing. JSF will internally convert this to _plain_query_sort_by_field in the POST payload.
In Bricks Builder, select your query loop element and set a Query ID under the JSF settings panel (the field labelled "Query ID" added by JSF when the element is marked as filterable).
Example: my-listing
All JSF filter widgets on the page must have the same Query ID set in their "This filter for" setting.
Add this to your theme's functions.php or a site-specific plugin.
add_filter('bricks/posts/query_vars', function($query_vars, $settings, $element_id, $element_name) {
// Target only your specific Bricks loop by its JSF Query ID
// Note: Bricks stores this under 'jsfb_query_id', not 'query_id'
$query_id = $settings['jsfb_query_id'] ?? '';
if ($query_id !== 'my-listing') return $query_vars;
// JSF sends filter data via POST during AJAX requests
// The _plain_query:: prefix becomes _plain_query_ in the POST key
$query_data = $_POST['query'] ?? [];
$sort_field = sanitize_key($query_data['_plain_query_sort_by_field'] ?? '');
if (empty($sort_field)) return $query_vars;
// Whitelist allowed field keys — never trust user input directly
$allowed_fields = ['weight', 'length', 'rating']; // add all your fields
if (!in_array($sort_field, $allowed_fields, true)) return $query_vars;
// Filter: only show posts that have a non-empty value for this field
$query_vars['meta_query'] = [
'relation' => 'AND',
[
'key' => $sort_field,
'compare' => 'EXISTS',
],
[
'key' => $sort_field,
'value' => '',
'compare' => '!=',
],
];
// Sort: numeric descending by the selected field
$query_vars['orderby'] = 'meta_value_num';
$query_vars['order'] = 'DESC';
$query_vars['meta_key'] = $sort_field;
return $query_vars;
}, 99, 4); // Priority 99 is critical — see note below
// Register the query var so WordPress doesn't strip it
add_filter('query_vars', function($vars) {
$vars[] = 'sort_by_field';
return $vars;
});
jsfb_query_id, not query_id
When you check $settings inside the hook, Bricks stores the JSF Query ID under jsfb_query_id. Using query_id will always return empty and your hook will silently skip every request.
To discover the correct key for your setup, temporarily dump the full settings array:
add_filter('bricks/posts/query_vars', function($query_vars, $settings, $element_id, $element_name) {
error_log(print_r($settings, true));
return $query_vars;
}, 10, 4);
When a user clicks a filter, JSF fires an AJAX request. The filter values are in $_POST['query'], not $_GET. Reading $_GET will always be empty on filter interactions.
_plain_query:: prefix transforms the POST key
Setting the Query Variable in JSF to _plain_query::sort_by_field results in the POST key being _plain_query_sort_by_field (the :: becomes _). Read it accordingly:
$query_data = $_POST['query'] ?? [];
$sort_field = sanitize_key($query_data['_plain_query_sort_by_field'] ?? '');
This is the most painful one. If you register the hook at the default priority of 10, JSF will run its own bricks/posts/query_vars hook afterward and overwrite your orderby back to date (the default).
Always use priority 99 or higher when your hook needs to be the final word on query arguments:
}, 99, 4); // Not 10. Not 20. Use 99.
The Bricks documentation shows 4 arguments: $query_vars, $settings, $element_id, $element_name. The last argument ($element_name) was added in Bricks 1.11.1. Always declare all 4 and pass 4 as the last argument to add_filter:
add_filter('bricks/posts/query_vars', function($query_vars, $settings, $element_id, $element_name) {
// ...
}, 99, 4);
User clicks radio → "Weight"
↓
JSF fires AJAX: POST query[_plain_query_sort_by_field] = weight
↓
bricks/posts/query_vars hook fires (priority 99)
↓
Hook reads _plain_query_sort_by_field from $_POST['query']
Validates against whitelist
Injects meta_query (EXISTS + not empty)
Injects orderby=meta_value_num, order=DESC, meta_key=weight
↓
Bricks executes WP_Query with modified args
↓
Results: only posts WITH a weight value, sorted highest → lowest
Other active filters (taxonomy, search) remain stacked — unaffected
Add temporary logging to trace exactly where things break:
add_filter('bricks/posts/query_vars', function($query_vars, $settings, $element_id, $element_name) {
error_log('HOOK FIRED — element: ' . $element_id);
error_log('jsfb_query_id: ' . ($settings['jsfb_query_id'] ?? 'NOT SET'));
$query_data = $_POST['query'] ?? [];
error_log('POST query: ' . print_r($query_data, true));
$sort_field = sanitize_key($query_data['_plain_query_sort_by_field'] ?? '');
error_log('sort_field extracted: ' . ($sort_field ?: 'EMPTY'));
// ... rest of your logic
error_log('Final query_vars: ' . print_r($query_vars, true));
return $query_vars;
}, 99, 4);
Check your PHP error log (usually at /wp-content/debug.log with WP_DEBUG_LOG enabled) after each filter interaction.
What
How
Pass selected field to server
JSF Radio filter with _plain_query::sort_by_field
Read the value in PHP
$_POST['query']['_plain_query_sort_by_field']
Target the right loop
Check $settings['jsfb_query_id']
Filter posts missing the field
meta_query with EXISTS + != ''
Sort numerically descending
orderby=meta_value_num + meta_key + order=DESC
Prevent JSF from overriding
Hook priority 99
Correct hook signature
4 params, last arg to add_filter is 4