Skip to main content

December 18th, 2022

How to Modify the Query Loop WordPress Gutenberg Block to Filter by a Custom Field

As WordPress continues to expand on its set of core blocks, you may find yourself using more and more native Gutenberg blocks in your builds, such as the Query Loop block. While it’s likely the user interface for this block will grow in future updates, it’s currently not the most intuitive if you want to modify the loop to do something involving metadata, such as grabbing all posts with a specific ACF custom field value. Let’s dive into the Query Loop block a bit and figure out how you can integrate a custom field created natively or through ACF to the logic driving the block.

What is the WordPress Query Loop block?

If you’ve never worked with the Query Loop Gutenberg block but have previous theme development experience, the Query Loop block is comparable in functionality to a basic query posts PHP loop that you might use in the classic way of WordPress theming. You’re cycling through a content type on your WordPress site and displaying each item’s content in a specified way. The loop has always been at the heart of grabbing content from WordPress and if you were learning to build themes in the pre-Gutenberg days, learning the loop was typically stressed as one of the most important first items to master.

Because the loop is such a key component of WordPress-based websites, full site editing (FSE) themes would struggle to exist without something like the Query Loop block. This block also opens the door for full site editing WordPress themes to build things like archive pages. Archive pages have been a challenge to customize with blocks before full site editing themes, though there have been some really clever workarounds to their limitations, such as creating block-based widget areas you could pull into these types of pages.

Full site editing themes are not the only theme type that can find value in the Query Loop block. If your theme isn’t completely converting over to the full site editing approach but is primarily leveraging the block editor for your content, a Query Loop block could still find a home featuring the latest blog posts or an events feed or similar.

Ultimately, the Query Loop block is giving you the ability to grab some content out of your WordPress site and run each piece of that content through a specified set up.

Modifying the Loop

It’s super common when working with WordPress to want to specify criteria for the type of content a query is returning to be used in a loop. You often don’t want all of a content type, but maybe you just need posts from a specific category or want to make a certain post type archive return a set number of posts different from other archive pages.

In a PHP query loop, if you wanted to make a more specific request for the content the loop was returning, you’d do this with query arguments in front of your loop. You could provide those arguments to the query using something like the pre_get_posts filter. In the Query Loop block, there are block settings that allow you to make some of these same arguments, such as specifying a post count or a category or tag to limit to. You access those settings by adding a Query Loop block then looking for the toolbar icon that looks like filters.

However, at least at the time of this writing, there’s not a control in the Query Posts block to grab only items with a specific meta key. So how could you go about doing it?

An Intro to Block Variations

If you’re filtering a WordPress query by a meta key, you’re often not wanting to filter every query globally by these arguments, but instead a specific query in a specific location. Block variations allow you to do something similar with blocks. They’re essentially saying that you want to take the core logic or functionality of a block but put a unique spin on it. Then you can use that block variation in the places where that unique set up is needed. So in this instance, you’d be creating a variation to take the concept at the heart of the Query Loop block — looping through a set of content — and apply meta arguments just to this special variation of the block, not all instances of the Query Loop block globally.

Creating a Custom Meta Query Loop Block Variation

For the sake of this example, we’re going to walkthrough creating a Query Loop block variation for upcoming events. This block variation needs to pull in items in the events custom post type with a date that is equal to or greater than today. This date is stored in an ACF date field called event_date that is set to store the date in Ymd format.

To do this, we’ll be focusing on:

  1. Registering a block variation for the meta query version of the Query Loop block
  2. Modifying that block variation’s logic & display to include the meta query arguments

Let’s get going!

Registering a Block Variation in JS

Gutenberg is a React-based editor so if you’re doing anything that impacts the block editor, odds are you’re going to be touching JavaScript to achieve it. Registering a block variation is not an exception to that rule. Where this JS file makes sense to exist will vary based on your theme build process. Maybe you will have a global editor.js file you pull into the block editor that has all your Gutenberg changes, maybe you’ll keep your block variations isolated to their own JS file, maybe you separate each new block into a custom plugin, etc.

To simplify this process as much as possible, the rest of this tutorial is going to be based on the assumption that you’re creating this JS file within a /js/ subfolder of your theme so the general location is:

/wp-content/themes/your-theme/js/block-variations.js

Inside of this JS file is where you’ll register the block variation via JS. When registering a block variation, you need a unique name for the variation you are creating. Assign this to a constant in your JS file ahead of the code registering the actual variation:

const VARIATION_NAME = 'upcoming-events-list';

wp.blocks.registerBlockVariation( 'core/query', {
  name: VARIATION_NAME,
  title: 'Upcoming Events List',
  description: 'Displays a list of upcoming events',
  icon: 'calendar-alt',
  attributes: {
    namespace: VARIATION_NAME,
    query: {
      postType: 'events',
      offset: 0,
      filterByDate: true
    },
  },
  isActive: [ 'namespace' ],
  scope: [ 'inserter' ],
  allowedControls: [ ],
  innerBlocks: [
    [
      'core/post-template',
      {},
      [
        [ 'core/post-title' ]
      ],
    ]
  ]
});

Let’s look closer at pieces in the above JavaScript:

name, title, description, icon

Within the registerBlockVariation piece, the way it starts out should feel familiar, referencing basics like the variation name (a unique name assigned to your variation), block variation title, a description, and an icon. For the icon, you can use a SVG or dashicons, much like on custom post types or when registering a regular custom block. This appears with this variation in the block editor.

attributes

Within the attributes array, you’ll see a namespace, which is pointed at our unique variation name, and query arguments. Inside of the query is where the post type is going to be assigned to events. In this variation, the post type is being set automatically by the variation vs. being pulled from a user input. Also, notice a filterByDate value has been added here, this will be referenced later.

isActive

isActive is used to let the block editor know that this block variation is being used. The Query Loop block exposes an attribute called namespace that was set with namespace: VARIATION_NAME under attributes. This lets Gutenberg know it is your specific variation only in the case it matches your custom namespace.

scope

By setting the scope to inserter, the block variation will be listed separately in the block list by the unique name and icon set when registering the variation.

allowedControls

In the case of this block variation, the post type and order are going to be built into the block to be set automatically so, other than choosing how many events to display, the block will be pretty plug-and-play. If building a block variation for a plugin, it’s likely that you’d want to build in custom controls for the user to enter in the post type name and/or the meta key as those wouldn’t necessarily be consistent. For the sake of this example, we’re betting on those being known items so this is being built for a specific project with a known custom post type name (events) and a known meta key (event_date.) If you’d like to add additional block controls to extend block functionality to user inputted data, the block editor guide covers a route to do so.

The lack of controls needed for this particular example continues on in what is defined here, by setting an empty array for allowedControls. These tie in to default controls you see when using the Query Loop block. The post type in this case will be assigned automatically but, depending on use case for a Query Loop variation, enabling some controls may make sense. Possibilities for this field include:

  • inherit: Shows the toggle switch for allowing the query to be inherited directly from the template.
  • postType: Shows a dropdown of available post types.
  • order: Shows a dropdown to select the order of the query.
  • sticky: Shows a dropdown to select how to handle sticky posts.
  • taxQuery: Shows available taxonomies filters for the currently selected post type.
  • author: Shows an input field to filter the query by author.
  • search: Shows an input filed to filter the query by keywords.

innerBlocks

Setting the post template that should be used for the Query Loop can be done with innerBlocks. innerBlocks should always start with core/post-template. Because this tutorial is focused on adding meta query arguments to the block logic, spending a lot of time on an elaborate display template for each event isn’t warranted so a call to core/post-title is all that is going to be used for each looped item for now, which would display the event name. It would make sense to also include a display of an event date, but displaying ACF data in this format includes its own set of considerations.

Calling the JS in Your Theme Functions

With the JS file for the block variation created, it now needs to be pulled into the block editor for use. To pull in this block-variations.js file into the block editor, the enqueue_block_editor_assets hook will be utilized. Notice how ‘wp-blocks’ is a dependency for the script, much like you could have been referencing jQuery as a dependency for your front-end theme JS in the past.

function wpfieldwork_editor_assets() {
  wp_enqueue_script(
    'wpfieldwork-block-variations',
    get_template_directory_uri() . '/js/block-variations.js',
    array( 'wp-blocks' )
  );
}
add_action( 'enqueue_block_editor_assets', 'wpfieldwork_editor_assets' );

Modifying the Query Arguments for the Block Variation

With these two pieces in place, when you go to add a block within your theme, you should see an Upcoming Events List as an option within your theme block category. However, you’ll notice if this block is inserted anywhere, the latest added events are what get returned, regardless of whether they are past or upcoming, as no logic referencing the ACF event_date custom field has been provided yet.

As a reminder, the goal is to pull only upcoming events in this block so, in terms of meta_query arguments, we’re looking at:

  • meta_key: The meta key would be the event_date field assigned to the events CPT
  • meta_value: The date for this field would be today’s date. Since event dates are stored in Ymd format, for simplicity’s sake, let’s grab today’s date in Ymd format with date(‘Ymd’)
  • meta_compare: The compare would be equal to or greater than

It also likely makes the most sense to then order the query by the meta key (event_date) in ascending order so that the next event would be listed first.

Making the Query Work on the Front End

On the front-end, the Query Loop block can be modified using the pre_render_block hook and it can be combined with the query_loop_block_query_vars hook to modify the query only in this block variation.

add_filter( 'pre_render_block', 'wpfieldwork_upcoming_events_pre_render_block', 10, 2 );

function wpfieldwork_upcoming_events_pre_render_block( $pre_render, $parsed_block ) {

  // Verify it's the block that should be modified using the namespace
  if ( !empty($parsed_block['attrs']['namespace']) && 'upcoming-events-list' === $parsed_block['attrs']['namespace'] ) {

    add_filter(
      'query_loop_block_query_vars',
      function( $query, $block ) {
        // get today's date in Ymd format
        $today = date('Ymd');

        // the meta key was event_date, compare to today to get event's from today or later
        $query['meta_key'] = 'event_date';
        $query['meta_value'] = $today;
        $query['meta_compare'] = '>=';

        // also likely want to set order by this key in ASC so next event listed first
        $query['orderby'] = 'meta_value';
        $query['order'] = 'ASC';
        
        return $query;
      },
      10,
      2
    );
  }
  return $pre_render;
}

After placing this function, you should see the front-end display of the Upcoming Events List variation is now displaying only upcoming events properly ordered by their event date. However, the backend view will still show the most recently added events, past or upcoming, without taking this into account so another function is needed to make the backend view correctly reflect the query, as well.

Making the Query Work on the Backend

The Query Loop block fetches its posts to show in the editor preview using the WordPress REST API. To get the correct events to display on the backend, we need to modify the REST API query involved. WordPress dynamically creates hooks for post types to do this that are in the format of: rest_{custom post type name}_query. To impact the events query, the hook would be rest_events_query. The query for the backend could be modified doing something like:

add_filter( 'rest_events_query', 'wpfieldwork_rest_upcoming_events', 10, 2 );

function wpfieldwork_rest_upcoming_events( $args, $request ) {
  // apply same meta arguments here
  $today = date('Ymd');
  $args['meta_key'] = 'event_date';
  $args['meta_value'] = $today;
  $args['meta_compare'] = '>=';
  $args['orderby'] = 'meta_value';
  $args['order'] = 'ASC';
  return $args;
}

After adding this, you’ll see the backend reflects the same order as the front-end. However, there’s one more issue to solve. The above adjusts every API request for the events post type to filter by these arguments so we need a way to limit the modification to just happen when the request is being made for the custom Query Loop block variation.

If you remember back in our JS file that registered the variation, a filterByDate option was added to the query argument. Any extra parameter in query is made available in the $request variable. Let’s revise the filter to check for it.

add_filter( 'rest_events_query', 'wpfieldwork_rest_upcoming_events', 10, 2 );

function wpfieldwork_rest_upcoming_events( $args, $request ) {
  
  // grab value from the request
  $dateFilter = $request['filterByDate'];
  
  // proceed if it exists
  // add same meta query arguments
  if ( $dateFilter ) {
    $today = date('Ymd');
    $args['meta_key'] = 'event_date';
    $args['meta_value'] = $today;
    $args['meta_compare'] = '>=';
    $args['orderby'] = 'meta_value';
    $args['order'] = 'ASC';
  }
  
  return $args;

}

With that piece in place, you should see your events CPT displayed in the block editor matching the meta query arguments, as well.

Hopefully, incorporating meta query data will be a bit more clear in the future, but it was an interesting challenge to work out doing it via a block variation in its current state. Hope something in here was helpful as you continue building with WordPress.

Additional Insight

  • Ryan Welcher released the Advanced Query Loop plugin that is worth checking out if you don’t want to roll your own functionality for ordering a Query Loop block by meta key or doing a date query or similar. He also built this plugin on his livestream and the videos are on YouTube if you want to check them out.
  • Justin Tadlock provides a helpful tutorial via the WordPress Developer blog that covers extending the Query Loop to support a book review grid, including the addition of block variation controls.

Get the latest in your inbox

Keep Reading