Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ Register custom query configurations in PHP that can be selected from a dropdown
- Queries work in both the editor preview and on the frontend
- Automatically hooks into all public post types via the REST API

### 7. Curated Posts

A "Curated Posts" inspector panel lets editors hand-pick and order the posts a Query Loop returns. Search is scoped to the block's current post type and writes to the core `query.include` attribute, with `query.orderBy = 'include'` so the editorial sequence is preserved on both the editor preview and the frontend.

**Behavior:**
- The list overrides whatever post set the block would otherwise return, including a selected `hmPreset` — useful for featuring specific items on a particular page while still defaulting to the preset elsewhere.
- An empty list is a no-op: the block falls back to its normal query.
- Search uses the post type's own REST collection endpoint (`/wp/v2/{rest_base}`), so any post type registered with `show_in_rest` is supported — there is no need to opt into `/wp/v2/search`.
- Posts already in the curated list are filtered out of the search results so they cannot be added twice.

## Installation

1. Upload the plugin to your `/wp-content/plugins/` directory
Expand Down
39 changes: 39 additions & 0 deletions hm-query-loop.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ function init() {
// Hook query_loop_block_query_vars to modify the query.
add_filter( 'query_loop_block_query_vars', __NAMESPACE__ . '\\filter_query_loop_block_query_vars', 11, 2 );

// Honor curated post lists (query.include). Priority 20 runs after the
// query presets filter (priority 15) so curated lists override presets.
add_filter( 'query_loop_block_query_vars', __NAMESPACE__ . '\\honor_query_include', 20, 2 );

// Hook into the_posts to track displayed posts and limit post-template posts.
add_filter( 'the_posts', __NAMESPACE__ . '\\track_displayed_posts', 10, 2 );

Expand Down Expand Up @@ -492,6 +496,41 @@ function modify_query_from_block_attrs( $query = [], $attrs = [] ) {
return $query;
}

/**
* Translate the core/query block's `query.include` attribute into `post__in`.
*
* Core's build_query_vars_from_query_block() recognizes `exclude` but not
* `include`, so curated post lists set via the Curated Posts inspector
* control (or directly in pattern markup) need promotion to real WP_Query
* arguments. Order is preserved via `orderby = 'post__in'`.
*
* Hooked at priority 20 so it runs after the query presets filter — when an
* editor sets a curated list on a block that also has an `hmPreset`, the
* curated list wins.
*
* @param array $query Query arguments to be passed to WP_Query.
* @param \WP_Block $block The child block whose render triggered this filter.
* @return array Modified query arguments.
*/
function honor_query_include( array $query, \WP_Block $block ): array {
$include = $block->context['query']['include'] ?? null;

if ( ! is_array( $include ) || empty( $include ) ) {
return $query;
}

$include = array_values( array_filter( array_map( 'intval', $include ) ) );

if ( empty( $include ) ) {
return $query;
}

$query['post__in'] = $include;
$query['orderby'] = 'post__in';

return $query;
}

/**
* Track displayed posts using the_posts filter.
*
Expand Down
Loading
Loading