Skip to content

Make Query block respect perPage option when sticky posts exist #41184

Closed
WordPress/wporg-showcase-2022
#145
@roo2

Description

@roo2

Currently if we have sticky posts, they will be prepended to the results of the query block, regardless of what was set in the perPage property.

For example, if we had two posts, one "sticky":

Screen Shot 2022-05-20 at 3 05 38 pm

And the following query in my homepage:

<!-- wp:query {"queryId":35,"query":{"perPage":"1" } } -->

Intuitively, I would expect to see only one post, but I am shown two:

Screen Shot 2022-05-20 at 3 04 36 pm

Why this happens

The Query block is built on top of the WP_Query class [WP_Query docs], which is where this behavior originates WP_Query issue marked as won't fix. This is the default behavior and probably won't be changed on WP_Query, but the core/query block does not have to match the interface of WP_Query exactly (and doesn't, in https://github.com/WordPress/gutenberg/pull/38909/files we change the arguments passed to WP_Query to give more intuitive results for "sticky": "only")

Suggested behavior

There are currently three options for the sticky setting.

Screen Shot 2022-05-20 at 3 37 24 pm

I suggest changing the behavior of the "Include" option (which generates the block markup: "sticky": ""). This is also the default behavior if no "sticky" option is set.

I suggest changing the Include option so that it includes sticky posts first in the list but will respect the perPage option. So a query with <!-- wp:query {"queryId":35,"query":{"perPage":"1", "sticky": "" } } --> would return a single post. Either the latest stickied post, or, if no sticky posts exist, the latest non-stickied post.

Screen Shot 2022-05-20 at 4 26 22 pm

Pseudocode implementation

I replaced the sticky property handling code in build_query_vars_from_query_block with the following:

$sticky = get_option( 'sticky_posts' );
if ( isset( $block->context['query']['sticky'] ) && ! empty( $block->context['query']['sticky'] ) ) {
    /* handle "sticky": "exclude" and "sticky": "only" as before.
     * ...
     */
} else {
    // handle "sticky": "", or case where "sticky" is not set
    $query['ignore_sticky_posts'] = 1;
    $query['post__in'] = $sticky;
}

This change resulted in the behavior I expected, selecting sticky posts first while respecting the "perPage" option. I also chcecked that order still works as before

I was able to test the change by hacking build_query_vars_from_query_block in wp-content/blog.php directly (tested on wpcom ;) ), but it looks like it can be implemented in gutenberg_build_query_vars_from_query_block in the lib/compat folder of gutenberg e.g. https://github.com/WordPress/gutenberg/blob/trunk/lib/compat/wordpress-6.0/blocks.php#L37

Metadata

Metadata

Assignees

No one assigned

    Labels

    Needs DecisionNeeds a decision to be actionable or relevant[Block] Query LoopAffects the Query Loop Block[Type] BugAn existing feature does not function as intended

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions