Skip to content

About

Filter controls for the query loop block, using the interactivity API

Topics

Resources

Contributing

Stars

124 stars

Watchers

19 watching

Forks

Repository files navigation

Query Loop Filters

image

This plugin allows you to easily add filters to any query loop block.

Provides 2 new blocks that can be added within a query loop block to allow filtering by either post type or a taxonomy. Also supports using the core search block to allow you to search.

Compatible with both the core query loop block and the Advanced query loop plugin (In fact, in order to use post type filters, use of the Advanced Query Loop plugin is required).

Easy to use and lightweight, built using the WordPress Interactivity API.

Usage

  • Add a query block. This can anyhere that the query block is supported e.g. page, template, or pattern.
  • Add one of the filter blocks and configure as required:
    • Taxonomy filter. Select which taxonomy to to use, customise the label (and whether it's shown), and customise the text used when none is selected. For a hierarchical taxonomy, choose how the tree is shown: a flat list (the default), nested beneath parents, or nested with each parent's children collapsed behind a toggle. Selecting a parent also matches posts in its children. Optionally hide terms with no results, and show each term's result count (see below).
    • Post type filter. Customise the label (and whether it's shown), as well as the text used when no filter is applied.
    • Search block. No extra options.

image

Result counts and hiding empty terms

The taxonomy filter's Terms panel has two options, both off by default:

  • Hide terms with no results leaves out any term that no post in the current results has, taking the loop's own query, the other filters and any search into account. A term the visitor has selected is always shown, so the control can always undo its own state, and a filter left with no terms is not rendered at all. With this on, the stored term counts play no part: a term is shown or hidden by its count in the current results alone.
  • Show result counts adds the count after each term's name, for example Cloud Security (12).

Counts follow the way the filters combine. Filters on different taxonomies narrow each other, so a term's count reflects every other active filter. Terms selected within the same taxonomy match any of them, so a taxonomy's own selection is left out of its counts: a count shows how many results selecting that term would add, not how many it shares with the terms already selected. A parent term's count includes posts in its descendants, as selecting it does.

Counts are calculated with one grouped query over the posts the loop would return, and are of published posts only, as an anonymous visitor would see them, so a signed-in user's private posts are not counted. They are cached in the object cache against the query for five minutes rather than cleared by every edit; change that with the query_filter_term_counts_cache_expiry filter.

Counting in the database gets slow on very large sites (hundreds of thousands of posts), and counts within a search are slow at any size, because the search itself is. Large sites should supply counts from a search index's aggregations, such as Elasticsearch's, by returning them from the query_filter_term_counts filter:

/**
 * @param int[]|null $counts     Counts keyed by term ID, or null to calculate them.
 * @param array      $query_vars WP_Query vars the terms are counted within: every filter
 *                               but this one, fetching only IDs.
 * @param string     $taxonomy   Taxonomy name.
 * @param WP_Block   $block      The taxonomy filter block.
 */
add_filter( 'query_filter_term_counts', function ( $counts, $query_vars, $taxonomy, $block ) {
	return $counts;
}, 10, 4 );

Linking to filtered results

Each filter is a URL parameter, so a link elsewhere on the page — outside the Query Loop, in a hero or a menu — can apply one. The parameter's name depends on the loop: query-{taxonomy} for a loop that inherits the main query, query-{id}-{taxonomy} for one with its own. Rather than build it by hand, ask for it with the loop's block context:

// "query-12-category", or "query-category" if the loop inherits its query.
$param = HM\Query_Loop_Filter\get_query_param( 'category', [
	'query'   => [ 'inherit' => false ],
	'queryId' => 12,
] );

The key is a taxonomy name, post_type, s for the search or page for pagination.

Installation

Using Composer

This plugin is available on packagist.

composer require humanmade/query-filter

Manually from Github.

  1. Download the plugin from the GitHub repository.
  2. Upload the plugin to your site's wp-content/plugins directory.
  3. Activate the plugin from the WordPress admin.

Built assets are not committed to main. Manual or Composer installs should track the release branch (or a tagged release), which contains the compiled build directory.

Local Development

This project uses wp-env to run a lightweight, containerized WordPress instance at localhost:3031 for testing purposes. The default username for the localhost environment is admin, with the password password.

These commands can be used to interact with the environment:

Command Purpose
npm run env:start Start the local environment at http://localhost:3031
npm run env:stop Turn off the local environment
npm run env:cli -- wp ... Run WP-CLI commands within the environment
npm run env:logs Open (and tail) the error logs for the application‡
npm run env:db Open the database in the mysql command line
npm run env:destroy Fully destroy the local environment (deletes container database)

‡ This command deliberately filters out GET/OPTIONS/HEAD/POST/PUT access log entries

Testing

End-to-end tests run against WordPress Playground, which boots in process — there is no environment to start or stop:

npm ci
npm run build   # tests run against build/, not src/
npm run test:e2e
Command Purpose
npm run test:e2e Run the Playwright suite
npm run test:e2e:debug Step through tests in the Playwright inspector
npm run test:e2e:watch Open the Playwright UI, rerunning on change
npm run playground:start Boot the same environment for manual testing, without running tests

The environment is described by blueprint.json and is shared between the test run and manual use. See tests/e2e/README.md for the fixture content and how to write tests.

Every pull request also gets a Preview in WordPress Playground button added to its description, which boots that PR's build with demo content already in place — no local checkout needed to try a change.

Release Process

Releases are cut by a manually-dispatched "Release" GH Actions workflow, which builds the plugin, stamps the version into query-filter.php, and creates an immutable vX.Y.Z tag pointing at the built, versioned code. That tag is published to Packagist and attached to a GitHub release as a downloadable ZIP.

To cut a new release:

  1. Choose the target version number using semantic versioning.
  2. On the "Release" GH Action page, click "Run workflow".
  3. Fill out the "Version" field without a leading v, e.g. 1.2.3.
  4. Click "Run workflow".

There is no "prepare release" pull request — the version number is supplied to the workflow and never lives on main, where the Version header is always the literal placeholder __VERSION__.

Separately, merges to main automatically build to the release branch. A project may track the release branch using Composer to pull in the latest built beta version. The release branch is not versioned and is not involved in cutting a tagged release.

See RELEASE.md for the full process, including what ships in the ZIP and how to cut a release by hand.

About

Filter controls for the query loop block, using the interactivity API

Topics

Resources

Contributing

Stars

124 stars

Watchers

19 watching

Forks

Releases

Packages

Contributors

Languages