Back to list
dev_to 2026年4月25日

JetSmartFilters と Bricks Builder を使ってカスタムフィールド値による投稿のフィルタリングとソート方法

How to Filter and Sort Posts by Custom Field Value Using JetSmartFilters + Bricks Builder

Translated: 2026/4/25 3:00:17
bricks-builderjetsmartfilterswordpress-developercustom-fieldajax-filter

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