Using pre_get_posts on a specific core/query block

I’m trying to apply the pre_get_posts to a specific core/query block, in order to programatically modify the number of posts displayed when a custom attribute is set. If I use the render_block hook, the block is already pre-rendered and so modifying the query using pre_get_posts has no effect. Is there any way to achieve this?

(I don't need help adding the custom attribute; I've done so already using block filters.)

Topic block-editor pre-get-posts wp-query Wordpress

Category Web


There's an open ticket for such a query args filter #54850.

In the meanwhile you can try this approach, replacing the render callback of the core/post-template block with a wrapper callback:

add_filter( 'block_type_metadata_settings', function( $settings ) {
    if ( $settings['parent'][0] !== 'core/query' ) {
        return $settings;
    }
    if ( $settings['render_callback'] !== 'render_block_core_post_template' ) {
        return $settings;
    }
    $settings['render_callback']= 'wpse_render_block_core_post_template';
    return $settings;
}); 

with the wrapper callback as:

function wpse_render_block_core_post_template($attributes, $content, $block ){
    $output = render_block_core_post_template( $attributes, $content, $block );
    return $output;
}

Now we have a better way to target the WP_Query instance within that core template.

Following are two such examples.

Example with pre_get_posts

Here's an example how to override the number of posts with the help of the pre_get_posts hook:

function wpse_render_block_core_post_template( $attributes, $content, $block ) {
    $cb = fn( $q ) => $q->set( 'posts_per_page', 2 );
    
    add_action( 'pre_get_posts', $cb, 999 );
    $output = render_block_core_post_template( $attributes, $content, $block );
    remove_action( 'pre_get_posts', $cb, 999 );

    return $output;
}

Example with block context

We could also change existing query arguments within build_query_vars_from_query_block(), like perPage, with:

function wpse_render_block_core_post_template( $attributes, $content, $block ) {
    $block->context['query']['perPage'] = 2;
    return render_block_core_post_template($attributes, $content, $block );
}

Then you can add further restrictions as needed to both approaches, from e.g. other block info, like queryId.


A modified version of the suggestion from @birgire works. Note that this applies to the core/post-template block, not the core/query block.

add_filter('block_type_metadata_settings', function ($settings, $metadata) {
    if ($metadata['name'] !== 'core/post-template') {
        return $settings;
    }
    if ($settings['render_callback'] !== 'render_block_core_post_template') {
        return $settings;
    }
    $settings['render_callback'] = 'wpse_render_block_core_post_template';
    return $settings;
}, 10, 2);

function wpse_render_block_core_post_template($attributes, $content, $block)
{
    $callback = fn ($query) => $query->set('posts_per_page', 2);

    add_action('pre_get_posts', $callback, 999);
    $output = render_block_core_post_template($attributes, $content, $block);
    remove_action('pre_get_posts', $callback, 999);

    return $output;
}

About

Geeks Mental is a community that publishes articles and tutorials about Web, Android, Data Science, new techniques and Linux security.