Skip to main content

Product Filters

Overview​

Product Filters lets customers narrow the shop by price, category, attribute, tag, rating and stock. It is one of the seven modules in the free core.

Two things separate it from a simple filter widget. The result counts beside each option are calculated and cached, so a customer sees how many products each choice would leave before clicking it. And the filtered state lives in the URL, under a configurable parameter prefix, so a filtered view can be shared, bookmarked and returned to.

It also takes a deliberate position on search engines: filtered pages are not indexed by default, which is the right default for avoiding thousands of near-duplicate URLs competing with the canonical category page.

It is the free core's filtering engine, available on every plan.

Availability​

ItemValue
Module keyproduct_filters
TierFree — part of the free core
Entitlement keyproduct_filters
Admin tabproduct-filters
Enabled optionaiowc_module_enabled_product_filters (off until turned on)
REST namespaceaiowc/v1

Enabling the module creates the two tables below and schedules the cache warm-up job.

registerHooks() performs no licence check of its own; being free-tier, the entitlement gate grants it on every plan.

Settings​

Stored in the bundled option row aiowc_pf_settings.

Stored keyDefaultMeaning
enable_on_shoptrueWhether filters appear on the main shop page.
enable_on_archivestrueWhether they appear on category and tag archives.
enable_ajaxtrueWhether filtering updates the grid without a page load.
seo_index_filteredfalseWhether filtered pages may be indexed. Off by default.
cache_ttl3600How long calculated counts are cached, in seconds.
url_param_prefixpf_The prefix on every filter parameter in the URL.
debounce_delay300Milliseconds waited before applying a change, so dragging a slider does not fire a request per pixel.
show_result_counttrueWhether the count is shown beside each option.
show_active_filterstrueWhether the applied filters are listed above the results.
scroll_to_resultstrueWhether the page scrolls to the grid after filtering.

Filter presets​

A preset is a saved filter configuration:

FieldMeaning
name, slug, descriptionWhat the preset is called.
context_type, context_valueWhere it applies — global by default, or a named context.
filters_configWhich filters it offers, as JSON.
display_configHow they are presented, as JSON.
statusdraft by default. A new preset is not live until published.
priorityWhich preset wins when several apply.

Caching and invalidation​

Counts are expensive, so they are cached with a context hash and an expiry, and warmed by a background job.

The cache is also invalidated by events, not only by time: the module hooks woocommerce_new_product, woocommerce_update_product and woocommerce_delete_product, plus created_term, edited_term and delete_term. Adding a product or renaming a category therefore drops the affected cache rather than leaving a stale count until it expires.

SEO handling​

With seo_index_filtered off — the default — the module marks filtered pages so they are not indexed, and keeps the canonical URL pointing at the unfiltered page. It integrates with Yoast where present, through wpseo_canonical, wpseo_opengraph_url and wpseo_robots, so the two do not disagree about what a filtered URL is.

Admin screen​

The Product Filters tab manages the presets — create, edit, duplicate, delete — shows an overview, lists the available product attributes to filter on, reports cache statistics and clears the cache, and edits the ten settings.

It also carries a query explain endpoint, which returns the query a filter combination produces. That is a genuine diagnostic: it answers why a filter returned what it did rather than leaving it to guesswork.

REST API endpoints​

All routes are on aiowc/v1 and answer with the { success, data, message } envelope.

MethodPathPurposePermissionRequired args
GET/product-filters/filterProducts matching a filter combination.Public, rate-limited–
GET/product-filters/countsCounts per option for a preset.Public, rate-limitedpreset_id
GET/product-filters/price-rangeThe price bounds for the current context.Public, rate-limited–
GET / POST/product-filters/presetsList and create presets.manage_woocommercename on create
GET / PATCH / PUT / POST / DELETE/product-filters/presets/{id}Manage one preset.manage_woocommerceid
POST/product-filters/presets/{id}/duplicateCopy a preset.manage_woocommerceid
GET/product-filters/attributesThe attributes available to filter on.manage_woocommerce–
GET/product-filters/overviewSummary of the configuration.manage_woocommerce–
GET/product-filters/cache/statsCache size and hit information.manage_woocommerce–
POST/product-filters/cache/clearEmpty the cache.manage_woocommerce–
GET/product-filters/debug/explainThe query a filter combination produces.manage_woocommerce–
GET / PATCH / PUT / POST/product-filters/settingsRead and write the settings.manage_woocommerce–

WooCommerce integration​

HookWhat the module does
pre_get_postsApplies the filters to the product query.
posts_requestUsed by the explain diagnostic to capture the query as built.
woocommerce_before_shop_loop / _after_shop_loopRenders the filter panel and the active-filter list.
woocommerce_new_product, _update_product, _delete_productInvalidates the affected caches.
created_term, edited_term, delete_termInvalidates caches when taxonomy changes.
wp_head, wpseo_canonical, wpseo_opengraph_url, wpseo_robotsKeeps filtered pages out of the index.
wp_enqueue_scriptsLoads the filter assets.

Shortcodes [aiowc_product_filters] renders the filter panel, [aiowc_filtered_products] the results grid, and [aiowc_active_filters] the list of what is applied — so the three parts can be placed independently in a custom layout.

Database schema​

TableHolds
{prefix}aiowc_filter_presetsThe presets: name, slug, description, context, filter and display configuration as JSON, status, priority, creator.
{prefix}aiowc_filter_cacheCached counts and results: a cache key and type, the value, a context hash and an expiry.

Background jobs​

HookWork
aiowc_product_filters_cache_warmupPre-calculates counts so the first customer does not pay for them.

The job runs on Action Scheduler.

Action hooks for integrators​

HookFired when
aiowc_track_eventThe module is enabled or disabled.
aiowc_capture_errorAn error is caught while filtering.

The module exposes no filter for adding a filter type of its own.

Entitlement limits​

product_filters is granted on every plan including the free one, with no cap on presets or cached rows.

Known gaps​

  • A new preset is created as a draft and is not live until published, which is easy to miss when a newly created preset does not appear on the shop.
  • Three modules hook pre_get_posts to change the product query — this one, Advanced Product Filters and Smart Product Search. Running more than one means several modules rewriting the same query, and the result depends on hook order. A store should choose one filtering module and one search module.
  • There is no filter for adding a filter type, so anything beyond price, category, attribute, tag, rating and stock requires modifying the module.
  • The explain diagnostic is REST-only; there is no screen that presents it.