Plugin documentation

Collection for WooCommerce

Turn WooCommerce product categories into smart collections. Add conditions to a category and matching products are assigned to it automatically, and removed when they stop matching. Categories without conditions are never touched.

Version 2.5.0 WooCommerce plugin Paid plugin Requires WordPress 6.5 Requires PHP 7.4 Tested up to 7.1 Updated 6 Oct 2026

1. Overview

Collection for WooCommerce turns WooCommerce product categories into smart collections, the way collections work in Shopify. You add conditions to a category, for example "Title contains summer", "Tag is equal to sale" or "Inventory is greater than 5", and choose whether a product must match all of them or any of them. The plugin then keeps the category up to date: matching products are added to it, and products that stop matching are removed from it.

A collection is an ordinary product category. Its archive page, menu entries, widgets and blocks work as before. The only difference is a small set of conditions stored on the category (see Term meta). Categories without conditions are never changed, so your manual categorisation stays as it is.

Products are checked as soon as they are created, edited, imported or when their stock changes. After you change a collection's conditions, all products are checked again in the background with Action Scheduler (the task runner bundled with WooCommerce). A full background check also runs every hour.

How it works

  1. Open Products → Categories, add or edit a category, and fill in Collection conditions. Use Preview matching products to see the result before you save.
  2. When you save, the plugin stores the conditions on the category and starts a background check of every product.
  3. For each product the plugin works out which collections it matches (only collections of the product's own language when Polylang is active), then sets the product's categories: the categories it has by hand, plus the collections it matches.
  4. From then on, a product is checked again whenever it changes, and every hour for all products.

At a glance

Twelve condition fields

Title, description, short description, SKU, category, tag, global attribute, price, on sale, inventory, stock status and product type, with operators that fit each field.

All or any

Require products to match every condition, or at least one. Up to 50 conditions per collection.

Instant and background updates

Products are re-checked when they are saved, imported or change stock. All products are re-checked after a rule change and every hour.

Preview before saving

See how many products match, which would be added and which would be removed, in steps that work for large catalogues.

Polylang aware

A collection only collects products of its own language, and a product only joins collections of its language.

Your manual categories stay

Only categories that have conditions are managed. Developer filters switch to add-only mode or adjust the result.

What it does not do

  • It does not change categories that have no conditions, and it does not create categories.
  • It does not check the front end. It works on the data: it sets product categories, and WooCommerce shows them as usual.
  • It has no settings screen. All choices are conditions on categories.
  • It does not assign products by price range of a category, by custom (local) product attributes, by custom fields or by variation attributes. The attribute condition uses global attributes (Products → Attributes) set on the product.
  • It does not touch products in the Trash, auto-drafts or revisions.
  • It does not remove products from a category when you delete all conditions. The category becomes a normal category and keeps its products.

2. Requirements

ItemRequirementNotes
WordPress6.5 or laterFrom the Requires at least line. Tested up to 7.1 (from readme.txt). Core behaviour described on this page was checked against WordPress 7.1.2.
PHP7.4 or laterFrom the Requires PHP line. The PHP intl extension is optional: when its Normalizer class exists, text is compared in Unicode normal form.
WooCommerce8.0 or later (header: WC requires at least: 8.0)Required. The plugin header says Requires Plugins: woocommerce. In WordPress 7.1.2 that makes WordPress refuse to activate the plugin while WooCommerce is not installed and active ("Error: <plugin name> requires 1 plugin to be installed and activated: ..."). The plugin starts only if the class WooCommerce exists when WordPress finishes loading plugins (hook plugins_loaded, priority 5). Otherwise it does nothing and shows no message. Tested up to WooCommerce 11.1 (header WC tested up to). WooCommerce 11.1.2 was checked for the behaviour described here.
Action SchedulerBundled with WooCommerceRuns the hourly check and the background batches. If its functions are missing, batches fall back to WP-Cron single events, but the hourly schedule is then not created.
Polylang or Polylang ProOptionalWith it, every language has its own collections. See Polylang.
ImportersOptionalWP All Import and the XAdapter product importer are supported through their own hooks. Other importers are covered through WooCommerce's product hooks and term changes (see When products are checked).
Who can use the rule builderCapability manage_product_terms or manage_woocommerceShop Managers and Administrators have these (WooCommerce 11.1.2). The same rule applies to the preview, the status panel and the "Re-check all products now" button. Saving the category itself also needs permission to edit that category (edit_term).

3. Installation

  1. Make sure WooCommerce is installed and active.
  2. Open Plugins → Add Plugin → Upload Plugin (in WordPress 7.1.2 the menu item is called "Add Plugin"), choose the zip file from your purchase, click Install Now and then Activate.
  3. Open Plugins → WpExperts Hub Licences and activate your licence key to receive updates (see below). The plugin works without a licence.
  4. Open Products → Categories, add or edit a category and use Collection conditions.

What activation creates

Nothing is created at activation: no tables, no roles, no pages and no options. Activation only clears the transient wxp_col_queue_checked so that the hourly schedule is created on the next page load. The plugin writes data when you save conditions (term meta on the category), when it runs its background check (options for the job state and a lock, transients, Action Scheduler actions), and when the licence client stores a licence (see Privacy).

Licence and updates

The plugin bundles the WpExperts Hub licence and update client (licence/class-wpxh-licence-client.php, client version 2.0.0). One screen manages every WpExperts Hub plugin on the site: Plugins → WpExperts Hub Licences. It needs the manage_options capability. The client is registered on every page load once the plugin has started (hook init), so it also works for scheduled update checks.

  1. Find your licence key in the purchase email, or under My Account → Downloads on wpexpertshub.com.
  2. Open Plugins → WpExperts Hub Licences. Each WpExperts Hub plugin has a card with its status ("Active" or "Not active"), the installed version and, when known, "version X is available".
  3. Paste the key into Licence key and click Activate licence. Characters other than letters, digits and hyphens are removed from the key before it is sent.
  4. To move the plugin to another live site, click Deactivate licence first ("Deactivate before moving the plugin to another live site.").
  5. If you cannot find your key, open "Can't find your key? Email it to me", enter the email address used for the purchase and click Send licence key. The licence server sends the email.
  • Updates come through WordPress. Whenever the licence server reports a newer version than the installed one, the plugin is added to WordPress's own update list, so it appears on the normal Dashboard → Updates and Plugins screens. When an update exists but the server returns no package for your site, WordPress shows "Automatic update is unavailable for this plugin. Activate your licence to enable updates." under the plugin.
  • The update information is cached. The answer of the licence server is kept in the site transient wpxh_licence_check_v2 for 12 hours. After a failed request the previous answer is kept and the check is retried after one hour. The cache is cleared when you activate or deactivate a licence, send a key request, or when WordPress finishes any install or update (the hook upgrader_process_complete).
  • A reminder on the Plugins screen. Users with manage_options see "Activate your WpExperts Hub licence to receive updates for: ..." with a Manage licences link on Plugins while any registered WpExperts Hub plugin has no active licence.
  • Links in the plugin row. The plugin adds Collections (opens Products → Categories) and "Activate License" or "Deactivate License" (the plugin's own wording, linking to the licence screen) under its name on the Plugins screen. The link text reads the key status of the option named after the plugin's folder, so it shows "Activate License" even for an active licence if the plugin folder is not named wxp-woo-collections.
  • The licence key only unlocks updates. No code of the plugin checks the licence status before assigning products or showing the rule builder. The status is used only for the link text, the licence screen and the Plugins screen notices. Whether the server returns an update package for a site is decided on the licence server.

What the licence client sends to wpexpertshub.com, and when, is listed under Data sent to wpexpertshub.com.

Updating

Update from Dashboard → Updates (needs an active licence) or upload the new zip over the old plugin. Saved conditions are kept (the 2.5.0 upgrade notice says so). When the plugin notices that its version changed, it replaces its recurring Action Scheduler action with a fresh one and deletes the old option _wphub_collection left by earlier versions. Versions before 2.4.0 scheduled a check every two minutes. That schedule is replaced by the hourly one.

Multisite

The plugin has no network admin screen. Conditions are term meta, so each site has its own collections, its own job state and its own scheduled actions. Since 2.4.0 it also loads when WooCommerce is activated on single sites rather than network-wide. Deleting the plugin removes its data from every site of the network (see What deactivation and uninstall remove).

Deactivating and deleting

Deactivating removes the plugin's scheduled actions (wxp_col_queue and wxp_col_queue_batch). The saved conditions and the job state stay, products keep the categories they have, and nothing is checked any more until you activate the plugin again. Deleting the plugin removes the plugin's data (see What deactivation and uninstall remove). The categories and the products in them stay as they are. Deleting does not remove the licence options, so deactivate your licence first if you want the activation released.

4. Quick start

  1. Open Products → Categories.
  2. Fill in the name of a new category, for example "Summer sale", and scroll to Collection conditions. To change an existing category, click its name instead.
  3. Under Products must match, choose All conditions or Any condition.
  4. Set the first condition: choose a product field (for example Tag), an operator (is equal to) and a value (sale). Click Add another condition for more.
  5. Click Preview matching products. A line under the buttons tells you how many products match and gives a few examples.
  6. Click Add new category (or Update on the edit screen). On the edit screen a green message confirms "Collection saved with N conditions (products must match all)." and, when the conditions changed, "Matching products are being added in the background."
  7. Watch the blue status panel at the top of Products → Categories: "Checking your products: X of Y done." When it reads "Last full check ... ago", every product has been checked.

5. Features

The Collection conditions builder

The builder appears on both category screens under Products → Categories: on the "Add new category" form (heading Collection conditions) and on the Edit category screen (a row named Collection conditions). It is shown only to users with manage_product_terms or manage_woocommerce. It starts with the text: "Optional. Products that match these conditions are added to this category automatically, and removed when they no longer match. Remove all conditions to keep a normal category."

  • Current size. On the Edit screen: "N products are in this category now." with a View products link to the product list filtered by the category.
  • Products must match. Two radio buttons, All conditions (the default) and Any condition, and a live count: "No conditions" or "N condition(s)" (counting rows that have a value).
  • Condition rows. Each row is numbered and has: a Product field drop-down, for the Attribute field a second drop-down "Select attribute", a Condition (operator) drop-down, a Value control, and a delete button (Delete condition). A new category starts with one row: Title, "is equal to", empty value.
  • Adapting rows. When you change the field, the operators and the value control change to fit: a text box (up to 255 characters) for text fields, a text box with suggestions for category, tag and attribute, a number box for price and inventory, and a drop-down ("Choose a value") for on sale, stock status and product type.
  • Suggestions. The value box of Category, Tag and Attribute shows a list of existing names when you click in it or type. You can still type a name that is not in the list. The list holds up to 500 names, most used first (changeable, see Settings). On the Edit screen the names are those in the language of the collection (Polylang).
  • Add another condition adds a row. The button is disabled with the tooltip "You have reached the maximum number of conditions." at 50 rows.
  • Delete condition removes a row. The last remaining row is not removed; its value is cleared.
  • Preview matching products (see Preview).

Under the buttons the builder says: "The conditions decide which products are in this category. Products you add to it by hand that do not match are removed on the next check; categories without conditions are never changed."

Condition fields and operators

The fields are listed here in the order of the drop-down.

FieldWhat is comparedOperatorsValue
TitleThe product name.Text operatorsText, up to 255 characters
DescriptionThe full description as saved, including any HTML in it.Text operatorsText
Short descriptionThe short description as saved.Text operatorsText
SKUThe product SKU (a product without SKU compares as an empty text).Text operatorsText
CategoryThe names of the product's categories (see Category conditions).Text operatorsCategory name, with suggestions
TagThe names of the product's tags.Text operatorsTag name, with suggestions
AttributeThe names of the product's terms of one global attribute (choose the attribute in the second drop-down).Text operatorsAttribute term name, with suggestions for the chosen attribute
PriceThe product's current price (the sale price while a sale is active). A variable product has the price of every variation.Number operatorsA number
On saleWhether the product is on sale now.is equal to, is not equal toYes or No
InventoryThe stock quantity (see Number conditions).Number operatorsA number
Stock statusThe product's stock status.is equal to, is not equal toIn stock, Out of stock or On backorder (the list WooCommerce provides)
Product typeThe product type.is equal to, is not equal toSimple product, Grouped product, External/Affiliate product, Variable product (the list WooCommerce provides, which other plugins may extend)

Text operators: is equal to, is not equal to, starts with, ends with, contains, does not contain. Number operators: is equal to, is not equal to, is greater than, is less than.

A condition that is incomplete or invalid is ignored: no value, a value that is not valid for the field (a number field with text, a choice that is not in the list), a value longer than 255 characters, an operator that does not belong to the field, or an Attribute condition without an attribute chosen. See Saving conditions.

How a condition is evaluated

Text conditions

Both sides of the comparison are prepared the same way before they are compared: the basic special-character entities (&amp;, &lt;, &gt;, &quot; and the apostrophe forms) are decoded with WordPress's wp_specialchars_decode() (other entities such as &eacute; are left as they are), text is put in Unicode normal form (when PHP's intl extension is available), runs of white space become one space, the text is trimmed and made lower case (multibyte safe). So the comparison ignores case and extra spaces. "is equal to" compares the whole text. "starts with", "ends with" and "contains" compare a part, and "is not equal to" and "does not contain" are the opposite of "is equal to" and "contains".

Tag, attribute and category lists

A product can have several tags (or terms of an attribute, or categories). The positive operators (is equal to, starts with, ends with, contains) match when any of the names matches. The negative operators match when none matches: "is not equal to" and "does not contain" therefore also match a product that has no term at all. For an Attribute condition the product's terms of the chosen attribute are used. If that attribute no longer exists, the condition never matches.

Category conditions

A Category condition compares category names. For this the product's categories are its own categories apart from the smart collections, plus the smart collections it matches in this check. A collection never counts itself as a category of the product before it matches. Parent categories are not added automatically: only the categories assigned to the product are compared. In add-only mode (see Filters) the product's current collections are not removed from this list.

A Category condition may name another smart collection ("Category is Sale"). Collections are evaluated together, in rounds, until nothing changes. That means a chain of collections is complete after one check, and two collections that exclude each other settle on one answer instead of switching back and forth. Without Category conditions the collections are evaluated in one round.

Number conditions

  • Price. The current price. For a variable product the prices of its variations are listed (each price once). WooCommerce supplies them for published variations only, and leaves out out-of-stock variations when WooCommerce → Settings → Products → Inventory → Hide out of stock items from the catalog is on (WooCommerce 11.1.2). Positive operators match when any variation price matches ("is greater than 20" matches a product with a variation above 20). "is not equal to" matches when none is equal. A product with no price (for example a grouped product, which has no price of its own) never matches a Price condition, whichever operator it uses.
  • Inventory. The stock quantity of a product that manages stock. For a variable product that does not manage stock itself, the total of the variations that do manage stock. A product that does not manage stock has no quantity, so no Inventory condition matches it, "is less than" and "is not equal to" included. Use Stock status for such products.
  • Numbers are compared as decimals. Two numbers are "equal" when they differ by less than 0.000001.
  • A value typed with a comma is understood: "1,5" is 1.5, "1,000" is one thousand and "1,000.50" is 1000.5. Text that is not a number is not accepted.

Choice conditions

On sale, Stock status and Product type compare one value from a list with "is equal to" or "is not equal to". A scheduled sale that starts or ends is applied by WooCommerce saving the product (checked in WooCommerce 11.1.2), which triggers a check. The hourly check is the safety net.

All or any

With All conditions a product must match every valid condition. With Any condition one is enough. A collection with no valid condition is not a collection: the category is left alone.

When products are checked

A product is checked at once when any of the following happens. The checks run at priority 999, after other plugins have had their say.

EventHook the plugin listens toWhich product
A product is created (admin, REST API, importers, WP-CLI)woocommerce_new_productThat product
A product is updated (edit screen, Quick Edit, bulk edit, REST API, importers)woocommerce_update_productThat product
WP All Import saves a postpmxi_saved_postThat product (other post types are ignored)
The XAdapter product importer imports a productxadapter_product_importedThat product
Stock changes (orders, stock edits)woocommerce_product_set_stock, woocommerce_variation_set_stockThe product, or the parent of a variation
A variation is created, updated or deletedwoocommerce_new_product_variation, woocommerce_update_product_variation, woocommerce_delete_product_variationThe parent product (a variation's price, stock or sale can move the variable product in or out)
Other code sets the categories, tags, attribute terms or the Polylang language of a product (importers, other plugins, WP-CLI)set_object_terms (marks the product), then shutdown at priority 5 (checks it)That product, when the request ends, unless a product save already checked it
You change a collection's conditions, rename or delete a category, tag or attribute term that a collection may use, or click "Re-check all products now"Saving the category; edited_term; delete_term; the admin buttonAll products, in the background (see The background check)
Every hourThe recurring Action Scheduler action wxp_col_queueAll products, in the background

Products with the status Trash, Auto-draft or Inherit are skipped. Drafts, pending, scheduled and private products are checked when they are saved. The background check and the preview look at products with the status Published or Private only.

If no category has valid conditions, every one of these checks stops at once and does nothing.

What the plugin changes on a product

  1. Which collections apply. Only collections whose language equals the product's language (see Polylang). Without Polylang every collection applies.
  2. Which collections match. The conditions are evaluated as described under How a condition is evaluated.
  3. The new category list. The product's current categories, minus every smart collection (also those of another language and those that no longer match), plus the collections it matches now. Categories without conditions are left as they are.
  4. The default category. A product is never left without a category: if the new list is empty, the product gets the default product category (the "Uncategorized" category), of its own language with Polylang. If the default category was the product's only category and it now joins a collection, the default category is dropped. The default category is not dropped if it is itself a smart collection.
  5. The filter. The list is passed through wxp_collection_product_categories (see Filters).
  6. Saving. Only when the list differs from the product's current list, the plugin replaces the product's categories, clears WooCommerce's cached data of the product, recounts the category totals and fires wxp_collection_product_updated.
The conditions decideA product you tick in a smart collection by hand, which does not match its conditions, is removed from that collection at the next check (usually immediately when you save the product).
Add-only modeTo make collections only add products and never remove them, return true from the filter merge_collection_categories. See Filters.

The background check

After you change conditions, after a rename or deletion of a term, when you click Re-check all products now, and every hour, the plugin checks all products with Action Scheduler (bundled with WooCommerce).

  • Batches. Products with the status Published or Private are read in ascending ID order, 50 at a time, and one batch runs at a time. A batch stops early after 20 seconds and the next batch carries on from the last ID. Walking by ID means products deleted during the check never make it skip another one. The sizes can be changed (see Settings).
  • One at a time. A lock (the option wxp_col_lock) keeps two batches from running at once. A lock left by a batch that died expires after two minutes.
  • A rule change restarts the check. A check that is running is restarted so the new conditions reach every product. A check that is queued and has not started yet already covers the change.
  • The hourly check. A recurring action wxp_col_queue (group wxp-collections) starts a full check every hour (changeable). It does nothing when a check made progress within the last 15 minutes. The first run is scheduled one minute after the schedule is created. The schedule is checked at most once an hour.
  • When no category has valid conditions, starting a full check cancels the queued batches, marks a running check as idle and does nothing else. The hourly action itself stays scheduled and finds nothing to do.
  • Without Action Scheduler's functions, see Requirements.

You can see the actions under Tools → Scheduled Actions (WooCommerce also shows them under WooCommerce → Status → Scheduled Actions). The hooks are wxp_col_queue (hourly) and wxp_col_queue_batch (one batch, with the run ID as its argument).

The status panel and "Re-check all products now"

On the product category screens (Products → Categories, list and edit) a blue notice appears as soon as at least one collection exists. It shows "N smart collection(s)" and one line:

  • While a check runs: "Checking your products: X of Y done." with a progress bar. The panel refreshes itself every four seconds while the check runs.
  • After a check: "Last full check time ago: X products checked, Y updated."
  • Otherwise: "Products are checked when they change and once an hour."

When no check is running the panel has a Re-check all products now button. It starts a full check and returns you to the category list with "All products are being checked in the background." If there is no collection with conditions, the message is "There are no collections with conditions yet, so there is nothing to check."

Preview matching products

Preview matching products shows what the conditions in the form would do, without saving anything. It reads the products in steps of 200 (Published and Private), so it works for large catalogues. While it runs, the button reads Stop and the line under it shows "Checking products… X / Y". The result reads, for example: "12 products match these conditions. 4 will be added. 2 products are in this category but do not match, and will be removed. For example: Product A, Product B."

  • With no valid condition: "Add at least one condition with a value to see which products match."
  • "will be added" and "will be removed" appear only for a category that already exists (the Edit screen). On the Add screen only the number and examples show. When nothing would change: "The category already holds exactly these products."
  • With Polylang only products of the collection's language are counted. On the Edit screen the collection's language is used, on the Add screen the language chosen in Polylang's language box.
  • The preview evaluates this collection on its own. A Category condition that names another smart collection does not see that collection in the preview, so such a condition can show fewer matches than the real check.
  • When some rows were typed but are not usable conditions, the result ends with "N condition is not valid and is ignored." (plural: "N conditions are not valid and are ignored.").
  • The preview only reads data. Any change in the form clears the old result.

Saving conditions

  • Where it saves. The conditions are saved when you save the category from the Add or Edit screen (hooks create_product_cat and edit_product_cat, priority 999), and only when the form carries the builder's hidden field wxp-condition-data and you may edit the category. Quick Edit, the REST API and code that updates a category do not carry the field, so they leave the saved conditions alone.
  • No valid condition deletes the stored conditions: the category is a normal category again, and its products stay in it.
  • Validation. Each row is checked for its field: numbers must be numbers, choices must be in the list, the operator must belong to the field, an Attribute row needs an attribute, text may be at most 255 characters. Markup is removed and white space is tidied. Percent signs followed by two hex digits, such as "20%AD", are kept. A collection keeps at most 50 conditions.
  • Notices (Edit screen). After saving you see "Collection saved with N condition(s) (products must match all|any)." plus " Matching products are being added in the background." when the conditions changed. When you removed all conditions: "All conditions were removed: this is a normal category again. Products that were added by the conditions stay in it." When a row was typed but is not valid: "N condition(s) was/were not saved because its/their value(s) is/are not valid for that field/those fields." The messages are kept for five minutes. The Add screen saves through AJAX and shows no message: the form simply resets to one empty row.
  • A warning before saving (Edit screen). If some rows have a value and others are empty, the first click on Update stops with: "N condition(s) has/have no value and will be ignored. Enter a value or delete it, or press Update again to save anyway." The empty boxes are marked. Click Update again to save.
  • A full check starts only when something changed. The plugin compares the new conditions with the old ones. Saving the category again without changing the conditions does not start a check.

The "Collection rules" column

The category list at Products → Categories has a Collection rules column. A normal category shows a dash. A smart collection shows a badge such as "2 rules (match all)" or "1 rule (match any)", counting the valid conditions.

Polylang

Polylang counts as active when the functions pll_get_post_language and pll_get_term_language exist.

  • Every language has its own collections. Add the conditions on the category of each language. The values are names in that language: the French category "Chaussures" uses French tag names, for example.
  • A collection collects only products of its own language, and a product only joins collections of its own language. A collection without a language collects products without a language. Without this rule Polylang would copy the collection into the language of every product it was assigned to (fixed in 2.5.0). A wrong-language assignment left by an older version is removed at the next check.
  • The default category given to a product that would have no category is the one of the product's own language.
  • Renamed terms update only the collections of the renamed term's language.
  • Suggestions and preview use the language of the collection.
  • Rules are read without Polylang's language filter, so the cached rules always hold the collections of every language (fixed in 2.5.0).
  • A product whose Polylang language is set after it was saved (for example through the REST API) is checked again when the request ends.

Renaming and deleting terms

  • Renaming a category, tag or attribute term (taxonomies product_cat, product_tag and the attribute taxonomies whose names start with pa_) updates the "is equal to" and "is not equal to" conditions that name the old name (case-insensitive) in the matching field, in the collections of the term's language. Other operators, such as "contains", are not rewritten. A full check then starts, because the new name can change what they match. This happens only when at least one collection exists.
  • Deleting such a term starts a full check, because products lose the term.
  • Deleting a product category clears the plugin's cache of rules (and WordPress removes the term meta of that category).

Who can do what

  • manage_product_terms or manage_woocommerce: see and use the builder, the preview, the status panel and the re-check button.
  • edit_term for the category: save conditions with the category form.
  • manage_options: the licence screen.
  • The product checks run for every saved product, whoever saves it.

Behaviour to know about

  • Conditions are evaluated against the product as WordPress and WooCommerce have it in the database after the save. Text comparisons use raw values (the title and descriptions as saved, with any markup in the description).
  • Category conditions compare names, not slugs or IDs. Two categories with the same name behave alike.
  • Products that only exist as drafts, pending or scheduled are evaluated when saved, but not by the background check or the preview.
  • Variations never join a collection themselves. Only the parent product has categories.
  • The plugin changes category assignments only. Menu order, visibility and everything else stays as it was.

6. Settings

The plugin has no settings screen and registers no settings. Every choice is a condition on a category, made in the builder on the Products → Categories screens. The tables list the form fields the builder submits, and the limits and timings that code can change.

Builder fields (per category)

Setting (label)Form fieldDefaultWhat it does
Products must match: All conditions / Any conditionwxp-coll-condition (all or any)All conditionsWhether a product must match every condition or at least one. Any other value is saved as all.
Product fieldwxp-condition-type[n]Title (for a new row)One of title, description, short_description, sku, category, tag, attribute, price, on_sale, inventory, stock_status, product_type.
Select attributewxp-condition-attr[n]NoneThe ID of a global attribute. Required for the Attribute field. Saved as an empty value for other fields.
Condition (operator)wxp-condition-args[n]The first operator of the field ("is equal to")One of is-equal-to, is-not-equal-to, is-greater-than, is-less-than, starts-with, ends-with, contains, does-not-contain, limited to those that belong to the field.
Valuewxp-text-box[n]EmptyText up to 255 characters, a number, or one value from the field's list. An empty value makes the condition ignored.
(hidden marker)wxp-condition-data1Tells the plugin that the form carries the builder. Without it the saved conditions are kept as they are.

Limits: at most 50 conditions per collection (rows beyond 50 are dropped), at most 255 characters per text value.

Limits and timings that code can change

SettingKeyDefaultWhat it does
Add-only modeFilter merge_collection_categoriesfalseWhen true, matching collections are only added to products and products are never removed from a collection.
Products per background batchFilter wxp_collection_batch_size50Values below 1 count as 1.
Seconds one batch may runFilter wxp_collection_batch_seconds20A batch stops after this time and the next one carries on.
Seconds between full checksFilter wxp_collection_queue_interval3600 (one hour)Read only when the recurring action is created. An existing schedule keeps its interval until it is re-created (the plugin re-creates it after it is updated to a new version, and after you deactivate and activate it again).
Names offered while typing a valueFilter wxp_collection_autocomplete_limit500The most used names are offered first.
Most conditions per collectionConstant Wxp_Woo_Collection::MAX_RULES50Fixed in the code.
Longest valueConstant Wxp_Woo_Collection::MAX_VALUE255Fixed in the code.
Preview stepFixed in the code200 productsPer request.
Status panel refreshFixed in assets/js/wxp-admin.js4 secondsWhile a check is running.

7. Developer reference

Filters

FilterParameters and returnWhen
merge_collection_categoriesBool, default false. Return true for add-only mode.Each time a product is checked.
wxp_collection_product_categories$new (int[] category IDs to set), $product_id, $matched (int[] collections the product matches), $current (int[] current category IDs). Return the list of category IDs.After the plugin has worked out the new category list, before it compares it with the current list and saves.
wxp_collection_batch_sizeInt, default 50.Each background batch.
wxp_collection_batch_secondsNumber, default 20.Each background batch.
wxp_collection_queue_intervalInt seconds, default HOUR_IN_SECONDS.When the recurring action is created.
wxp_collection_autocomplete_limitInt, default 500.When the builder loads its suggestion lists.

Add-only mode, in a theme's functions.php or a snippet plugin:

add_filter( 'merge_collection_categories', '__return_true' );

Keep products in category 25 once they are in it, even when no collection matches:

add_filter( 'wxp_collection_product_categories', function ( $new, $product_id, $matched, $current ) {
	if ( in_array( 25, $current, true ) && ! in_array( 25, $new, true ) ) {
		$new[] = 25;
	}
	return $new;
}, 10, 4 );

Larger batches, a shorter time limit and a full check every six hours (the interval is read when the schedule is created):

add_filter( 'wxp_collection_batch_size', function () { return 100; } );
add_filter( 'wxp_collection_batch_seconds', function () { return 10; } );
add_filter( 'wxp_collection_queue_interval', function () { return 6 * HOUR_IN_SECONDS; } );

Actions

ActionParametersWhen
wxp_collection_product_updated$product_id, $new (int[] new category IDs), $current (int[] previous category IDs)After the plugin changed the categories of a product.
wxp_collection_pass_completed$job (array: run, status, last_id, processed, total, changed, started, updated, finished, source)When a full check of all products has finished.
add_action( 'wxp_collection_pass_completed', function ( $job ) {
	error_log( sprintf( 'Collections: %d products checked, %d changed.', $job['processed'], $job['changed'] ) );
} );

The scheduled hooks wxp_col_queue and wxp_col_queue_batch are the plugin's own Action Scheduler hooks. Do not call them yourself.

AJAX and admin-post actions

ActionWho and what it needsWhat it does
wxp_col_preview (AJAX)Logged-in users with manage_product_terms or manage_woocommerce. POST field nonce (nonce action wxp_col_admin). Otherwise HTTP 403 "You are not allowed to do this."Counts the products matching the conditions of the form, 200 products per call. Parameters: the builder's fields, term_id, lang, after_id, acc, samples. Returns done, last_id, acc, samples, ignored and, when done, message. HTTP 404 "Category not found." for an unknown term_id.
wxp_col_status (AJAX)The same capability and nonce.Returns whether a check is running and the HTML of the status panel.
wxp_col_recheck (admin-post.php)Logged-in users with the capability above. Nonce action wxp_col_recheck (in the link). Without the capability: HTTP 403 "Sorry, you are not allowed to do that." With a missing or expired nonce WordPress shows its own 403 page, "The link you followed has expired."Starts a full check and redirects to edit-tags.php?taxonomy=product_cat&post_type=product with a notice.

Term meta

KeyStored onValue
_wxp_term_conditionProduct categories (taxonomy product_cat)An array: type (all or any) and args, a list of rules, each with type (the field), condition (the operator), compare (the value) and attribute (attribute ID as a string, or empty). Absent when the category has no conditions.

Options, transients and scheduled actions

NameKindContent
wxp_col_jobOption (not autoloaded)State of the background check: run, status (idle, running or done), last_id, processed, total, changed, started, updated, finished, source (rules, term, manual or hourly).
wxp_col_lockOption (not autoloaded), short-livedTime stamp of the batch that is running. Expires after two minutes.
wxp_col_versionOption (not autoloaded)The plugin version that last set up the schedule.
_wphub_collectionOptionLeft by earlier versions. The plugin deletes it.
_wxp_col_settingsTransient, 12 hoursThe cached conditions of all collections. Cleared whenever you save conditions, rename a term in the conditions, or delete a product category.
wxp_col_queue_checkedTransient, 1 hourFlag that the hourly schedule was checked.
wxp_col_notice_<user id>Transient, 5 minutesThe message shown once after saving a collection or re-checking.
wxp_col_queueAction Scheduler recurring action, group wxp-collectionsStarts the hourly full check.
wxp_col_queue_batchAction Scheduler async action (or a WP-Cron single event without Action Scheduler)Checks one batch of products. Its argument is the run ID.
_wxp-woo-collections_licence_keyOption (not autoloaded)The licence key, after a successful activation.
_wxp-woo-collections_key_statusOption (not autoloaded)active after activation. Set to inactive when the licence server says the licence is no longer active here. Removed on deactivation.
wpxh_licence_check_v2Site transient, 12 hoursThe cached answer of the licence server, shared by all WpExperts Hub plugins.
wpxh_licence_msg_<user id>Transient, 2 minutesThe message shown once after you use the licence screen.

The plugin creates no tables of its own.

Core, WooCommerce and importer hooks the plugin attaches to

HookPriorityPurpose
woocommerce_new_product, woocommerce_update_product999Check the product after it is saved.
pmxi_saved_post, xadapter_product_imported999Check a product after WP All Import or the XAdapter importer saved it.
woocommerce_product_set_stock, woocommerce_variation_set_stock999Check the product (or the parent of the variation) after a stock change.
woocommerce_new_product_variation, woocommerce_update_product_variation, woocommerce_delete_product_variation999Check the parent product.
set_object_terms20Mark a product whose categories, tags, attribute terms or language were set by other code.
shutdown5Check the marked products at the end of the request (at most 500 rounds).
edit_terms, edited_term, delete_term10Follow renamed terms and re-check after a deleted term.
delete_product_cat10Clear the cached rules.
init10Register the licence client; make sure the hourly action is scheduled.
plugins_loaded5Start the plugin when WooCommerce is present.
before_woocommerce_init10Declare compatibility with custom_order_tables (HPOS), cart_checkout_blocks and product_block_editor. The plugin works on products and categories only.
product_cat_add_form_fields, product_cat_edit_form_fields999Draw the builder.
create_product_cat, edit_product_cat999Save the conditions.
manage_edit-product_cat_columns, manage_product_cat_custom_column20The "Collection rules" column.
admin_enqueue_scripts999Load the builder's script and style on the category screens only.
admin_notices10Messages after saving and the status panel.
plugin_action_links_wxp-woo-collections/wxp-woo-collections.php10The "Collections" and licence links.

All product checks end in the method Wxp_Woo_Collection::apply_collections( $product_id ), which you can call from your own code to re-check one product. The function Wxp_Woo_Collection() returns the plugin object. Its other public methods are not a documented interface and can change.

Screen elements, scripts and constants

  • Script wxp_coll_admin_script (assets/js/wxp-admin.js, depends on jquery and jquery-ui-autocomplete, localised object wxp_colls_i18n) and style wxp_coll_admin_style (assets/css/wxp-admin.css) load on the screen with ID edit-product_cat only. They use Dashicons and load no external files.
  • Constant WXP_COLL_VERSION (2.5.0). The licence client reads WPXH_LICENCE_SERVER if you define it, and has the filters wpxh_licence_server and wpxh_licence_sslverify. Certificate checks are switched off only when the licence server host ends in .local, .test or .localhost, or is localhost or 127.0.0.1.

File layout

FilePurpose
wxp-woo-collections.phpThe engine: fields, evaluation, applying collections, term changes, the background check.
classes/class-wxp-woo-collection-admin.phpThe builder's data, saving, preview, column, notices and status panel.
view/add.php, view/edit.php, view/builder.php, view/row.phpThe markup of the builder.
assets/js/wxp-admin.js, assets/css/wxp-admin.cssThe builder's behaviour and style (including a layout for narrow screens and right-to-left).
licence/class-wpxh-licence-client.phpThe bundled licence and update client.
uninstall.php, languages/Clean-up on delete; translation template (text domain wxp-woo-collections).

8. Privacy

What is stored

  • Term meta _wxp_term_condition on each smart collection: the field names, operators and values you typed. No personal data is involved unless you type some into a condition.
  • Options and transients for the background check (wxp_col_job, wxp_col_lock, wxp_col_version, _wxp_col_settings, wxp_col_queue_checked, wxp_col_notice_<user id>). They hold counts, time stamps and the cached conditions.
  • Action Scheduler records. Every scheduled action of the plugin is a record in Action Scheduler's own tables, with the hook name, the run ID and the group wxp-collections. Action Scheduler keeps its own history according to its own settings.
  • Licence data, if you activate a licence: the licence key and its status as options, and the cached licence-server answer as a site transient (see Options, transients and scheduled actions).
  • The plugin changes product category assignments. It sets no cookies, writes no log, adds nothing to the front end and does not read customer or order data.
  • It does not register privacy policy text or personal data exporters or erasers.

Data sent to wpexpertshub.com

Only the bundled licence client contacts another server. It sends JSON by HTTP POST to https://wpexpertshub.com/wp-json/wphub-licence/v1/<route> (the server can be changed with the WPXH_LICENCE_SERVER constant or the wpxh_licence_server filter), with a 20 second timeout. The routes and what each one sends:

RouteWhenData sent
activateYou click Activate licence.The plugin slug (wxp-woo-collections), the licence key and your site address (site_url()).
deactivateYou click Deactivate licence.The slug, the saved key and your site address. The key is removed from the site even when the server cannot be reached.
send-keyYou use "Can't find your key? Email it to me".The slug and the email address you typed.
checkWhen WordPress refreshes its plugin update list (pre_set_site_transient_update_plugins, which includes WordPress's scheduled checks and Dashboard → Updates → Check again), and when the plugin's "View details" window is opened, unless a cached answer for the same data is younger than 12 hours (one hour after a failed request).Your site address and, for every WpExperts Hub plugin registered on the site (not only this one): the plugin slug, the licence key (empty if none) and the installed version.

WordPress adds its usual User-Agent header to every remote request: "WordPress/<version>; <site address>" (checked in WordPress 7.1.2). The plugin sends no product, category, order or customer data. What the licence server stores or logs cannot be seen from the plugin's code.

What deactivation and uninstall remove

  • Deactivation removes the scheduled actions wxp_col_queue and wxp_col_queue_batch. It keeps the conditions, the job state and the licence data.
  • Uninstall (deleting the plugin on the Plugins screen) removes, for the site (for every site of a multisite network): the options wxp_col_job, wxp_col_lock, wxp_col_version and _wphub_collection; the transients _wxp_col_settings, wxp_col_queue_checked and every wxp_col_notice_ transient; the scheduled actions wxp_col_queue and wxp_col_queue_batch (when Action Scheduler's functions are still available at that moment); and the term meta _wxp_term_condition of every category. The categories and the products in them stay as they are, but the conditions are gone and the categories become normal categories.
  • Uninstall does not delete the licence options, the licence transients or the activation on the licence server. Deactivate the licence first.

9. Troubleshooting

ProblemLikely cause and fix
Products do not join the collection.Check the Collection rules column (a dash means no valid condition is saved). Common reasons: a condition has no value, or an Attribute condition has no attribute chosen (those rows are ignored); All conditions is chosen but one condition cannot match; the product's tag or attribute names do not equal the typed value; with Polylang the product's language is not the collection's language; the product is in the Trash. Use Preview matching products to test, then Re-check all products now.
The Collection conditions box is missing.WooCommerce must be active (the plugin does not start without it, and shows no message). Your user needs manage_product_terms or manage_woocommerce. The box is on Products → Categories only.
"N conditions have no value and will be ignored..." stops the save.Some rows have a value and others are empty. Fill them in or delete them, or press Update again to save anyway.
"N conditions were not saved because their values are not valid for those fields."A value does not fit its field: text in a number field, a choice that is not in the list, more than 255 characters, an operator that does not belong to the field. Correct it and save again.
A category I added by hand disappeared from a product.Only categories that have conditions are managed. If the category you ticked is a smart collection and the product does not match, the product is removed from it. Remove the conditions from the category if you want it to be a normal category.
A product lost "Uncategorized".By design. When a product that only had the default category joins a collection, the default category is dropped. If a product would have no category, it gets the default category back.
Products are never removed from a collection.Add-only mode may be on (code returns true for merge_collection_categories). Remove that filter.
An Inventory condition matches nothing, or fewer products than expected.Inventory matches only products that manage stock (for variable products, the total of the variations that do). A product that does not manage stock has no quantity. Use Stock status for those products.
The preview shows a different number than the collection ends up with.The preview evaluates this collection alone, so a Category condition that names another smart collection does not see it. It also counts only Published and Private products, and (with Polylang) only products of the collection's language.
The panel says "Checking your products" for a long time.Action Scheduler runs the batches. Open Tools → Scheduled Actions and look for wxp_col_queue_batch. If actions stay pending, check that WordPress's scheduled tasks (WP-Cron) can run on your site. A lock left by a failed batch expires after two minutes, and the hourly check restarts a check that made no progress for 15 minutes.
With Polylang, collections appear in the wrong language or in several languages.Before 2.5.0 a collection could be copied into every language. Update to 2.5.0 and run Re-check all products now: wrong-language assignments are removed at the next check. Add the conditions on the category of each language.
I renamed a tag and a condition stopped working.Since 2.5.0 the "is equal to" and "is not equal to" conditions follow the new name. Other operators ("contains", "starts with"...) are not rewritten: edit them yourself. A full check starts after every rename.
My conditions disappeared after I used Quick Edit.They should not. Quick Edit, the REST API and code that updates a category do not carry the builder's field, so the saved conditions are kept (fixed in 2.4.0). If they are gone, check whether the plugin was deleted (uninstall removes them) or the category was recreated.
A large catalogue is slow to re-check.The check runs in the background in small batches and never blocks a page. Tune wxp_collection_batch_size and wxp_collection_batch_seconds. A full check is also repeated every hour (wxp_collection_queue_interval).
No updates appear, or no Licence card.Open Plugins → WpExperts Hub Licences and activate your key. Cached answers last up to 12 hours: use Dashboard → Updates → Check again after activating.

10. FAQ

Will the plugin remove the categories I assigned by hand?

No. Only categories that have conditions are managed by the plugin. A product that only had the default "Uncategorized" category loses it when it joins a collection, and a product that would be left without any category gets the default category (of its own language with Polylang). A product you add to a smart collection by hand that does not match its conditions is removed again on the next check: the conditions decide what is in the category.

When are products assigned?

Immediately when a product is created or updated (admin, Quick Edit and bulk edit, REST API, imports) or its stock changes, and for all products in the background after you change a collection's conditions and every hour. See When products are checked.

Do products leave a collection when they stop matching?

Yes. For example, with an "Inventory is greater than 5" condition a product is removed once its stock drops to 5 or below. Add-only mode (the merge_collection_categories filter) keeps products in.

Can products only be added, never removed?

Yes. Add add_filter( 'merge_collection_categories', '__return_true' ); to your theme or a snippet plugin.

How do "is not equal to" and "does not contain" work for tags, categories and attributes?

They match products that have no tag, category or attribute term equal to (or containing) the value, including products without any term.

Which products match an Inventory condition?

Products that manage stock (for variable products: the total of their variations that manage stock). A product that does not manage stock has no quantity, so no Inventory condition matches it. Use the Stock status field for those.

Can a collection use another collection?

Yes. A "Category is ..." condition can name another collection. Collections are evaluated together, so a chain of collections is complete after one check, and two collections that exclude each other settle on one answer.

How does it work with Polylang?

Add the conditions on the category of each language (the values are names in that language). A collection only collects products of its own language, so the English conditions never put English categories on French products.

What happens when I rename or delete a category, tag or attribute term that a condition uses?

Renaming it updates the "is equal to" and "is not equal to" conditions that name it, and deleting it re-checks the products. Both start a background check.

My store has thousands of products. Is the check slow?

The check runs in the background in small batches (one at a time, walking the products by ID) and never blocks a page. Use the filters wxp_collection_batch_size (default 50) and wxp_collection_batch_seconds (default 20) to tune it.

Do I need a special page for collections?

No. Collections are regular product categories, so their archive pages, menus, widgets and blocks work as usual.

Does a scheduled sale move a product in or out of an "On sale" collection?

In WooCommerce 11.1.2 the sale start and end are applied by saving the product, which triggers a check. The hourly full check is the safety net.

What is removed when I delete the plugin?

The conditions on all categories, the plugin's options, transients and scheduled actions, on every site of a network. The categories and their products stay. The licence options remain, so deactivate your licence first.

Does the plugin work without a licence?

Yes. No code checks the licence before assigning products. The licence key is used for the update information and the update package.

How do I activate my licence and get updates?

Open Plugins → WpExperts Hub Licences, paste the key from your purchase email (also in My Account → Downloads on wpexpertshub.com) and click Activate licence. New versions then appear on the normal Dashboard → Updates screen.

Can I control the order of products inside a collection?

Not with this plugin. The order of products on a category page is decided by WooCommerce. Category Products Reorder for WooCommerce adds drag and drop ordering per category.

11. Changelog

2.5.0 - 06/10/2026

  • Fix - Polylang: the cached rules only held the collections of the language of the request that filled the cache, so the collections of the other languages stopped working for up to 12 hours. The rules are now read without the language filter.
  • Fix - Polylang: the rules of one language were applied to products of every language and Polylang then created a stray copy of the collection in each language. A collection now only collects products of its own language, and wrong-language assignments left by older versions are removed on the next check.
  • Fix - Polylang: a product that left its last collection received the default category of the default language; it now gets the default category of its own language (and a placeholder default category is dropped correctly).
  • Fix - "Inventory" conditions treated products that do not manage stock as having 0 in stock, so "less than 5" collected every such product. They have no quantity and no longer match.
  • Fix - Collections that use other collections ("Category is ...") needed several passes, and two collections that exclude each other were switched on and off on every pass. They now settle in one pass.
  • Fix - The background pass could skip products when products were deleted while it ran, could run twice at once, and read the products with a language filter. It now walks the products by ID in time-boxed batches, one batch at a time, and a rule change restarts it so the new rules reach every product.
  • Fix - Rule values were not validated per field: non-numeric inventory values, text operators on number fields and values of any length or number were saved. Invalid conditions are refused (with a notice), values are limited to 255 characters and a collection to 50 conditions. Percent signs followed by two hex digits (for example "20%AD") are no longer removed from values.
  • Fix - Renaming a category, tag or attribute term left the conditions that named it silently broken; they now follow the new name. Deleting a term re-checks the products.
  • Fix - Terms set by other code (importers such as WP All Import, other plugins, WP-CLI) did not re-check the product until the next hourly check; variation price, stock and sale changes now re-check the variable product, and a product whose Polylang language is set after it was saved (REST API) is checked again when the request ends.
  • New - More conditions: short description, SKU, price, on sale, stock status and product type.
  • New - "Preview matching products" shows how many products match the conditions in the form (and how many would be added or removed) before you save, in steps that work for large catalogues.
  • New - Status of the background check on the Products > Categories screens with a "Re-check all products now" button; a message after saving a collection tells how many conditions were saved and which were ignored.
  • New - A condition without a value is flagged once before the category is saved (it would be ignored).
  • New - Uninstall removes the plugin's options, scheduled actions and the conditions stored on categories (the categories and their products stay).
  • Tweak - New condition builder: numbered conditions with the operators and the value field that fit the field (number, choice list or text), delete buttons that work with the keyboard, a count of conditions, a layout for phones and RTL. The icon font was replaced by Dashicons.
  • Tweak - The "Collection rules" column shows the number of conditions as a label; the category edit screen shows how many products are in the category.
  • Tweak - The autocomplete lists of the value field show the names in the language of the collection, are limited to 500 names (most used first) and the rules are read with a single query.
  • Tweak - Checking a product against five collections costs about ten database queries (the languages of all collections are loaded together).

2.4.0 - 27/09/2026

  • Security - New licence client: licence data is exchanged as JSON only (the old client unserialized server responses) and a failed server request can no longer cause a fatal error.
  • Enhancement - One "Plugins → WpExperts Hub Licences" screen manages the licences of all WpExperts Hub plugins (previously only one plugin's licence page could be opened when several were installed). Existing activations are kept.
  • Enhancement - Updates are delivered through the standard WordPress update screen for sites with an active licence; "Email me my key" added.
  • Fix - "contains" conditions never matched (missing return value).
  • Fix - Products matching a collection lost every manually assigned category; now only categories with rules are added/removed, and products that stop matching are removed from the collection.
  • Fix - Saving a category from Quick Edit or programmatically erased its conditions; empty condition rows are no longer saved.
  • Fix - Stock changes did not re-check products (rules were not loaded) and variation stock changes were ignored.
  • Fix - Products are now re-checked immediately when they are created or updated, instead of only by the background job.
  • Fix - "is not equal to" / "does not contain" for tags, categories and attributes matched products that had any other term.
  • Fix - Rule values, tag and attribute names are escaped in the admin; inline script printed twice; jQuery UI Autocomplete was not loaded; the edit screen could not add "Category" conditions; adding a row after deleting one could overwrite another row.
  • Fix - Rule input is sanitised and validated.
  • Fix - Plugin was not loaded on multisite when WooCommerce was activated per site.
  • Fix - A condition on a deleted attribute caused PHP warnings.
  • Tweak - The background job no longer loads every product ID every 2 minutes; it runs in chained batches after rule changes and hourly, and does nothing when no collection has rules. Scheduled actions are removed on deactivation.
  • Tweak - Inventory rules use the total stock of variations for variable products that do not manage stock at product level; the value field is numeric and only valid operators are offered per field.
  • Tweak - "Collection rules" column in the product categories list.
  • Tweak - Declared compatibility with HPOS, Cart/Checkout blocks and the product block editor; Requires Plugins header; tested with WordPress 7.1 and WooCommerce 11.1.
  • Tweak - Plugin renamed to "Collection for WooCommerce"; text domain wxp-woo-collections.

2.3

  • Previous release.

12. Support

Email support@wpexpertshub.com. Please include:

  • Your order ID.
  • The plugin version (this page describes 2.5.0), your WordPress, WooCommerce and PHP versions, and your Polylang version if you use it.
  • The collection's conditions (a screenshot of the Collection conditions box helps), and the name or ID of a product that does not behave as expected.
  • The text of the blue status panel on Products → Categories, and what you already tried.

More plugins and documentation are at wpexpertshub.com.