How to link filtered products to WooCommerce product variations
If you are using WooCommerce product variations with FacetWP, you can filter these variations with facets that have product attributes set as their data source. See this section on our WooCommerce page to learn how to set this up.
This tutorial will explain a solution for letting the filtered products link directly to the selected product variation, and highlight this variation on the product page as shown in the image below.
The goal: automatic direct links to product variations based on facet selections
WooCommerce has built-in support for direct links to product variations, where the variation is pre-selected when you arrive on the product page. These kind of links looks like this:
/product/hoody/?attribute_pa_size=medium
If this product has a global attribute size
, the above link will automatically set “medium” as the selected variation. A user only needs to click “Add to cart” to add this variation to his/her cart.
You can also have variations based on multiple attributes, for example, “medium” + “red”. This link will select “medium” and “red” on the product page:
/product/hoody/?attribute_pa_size=medium&attribute_pa_color=red
We can leverage these links to let products that are filtered with a facet based on attributes, link directly to the selected variation(s) attribute:
Example: link to size and color product variations
To understand how product variations work with FacetWP, and to set up the basics, first read these instructions.
Create global or product-level attributes
In this example we will work with two global product attributes, size
and color
, and we assume products have been set up as variable products with variations based on these two attributes:
Global product attributes can be created in Products > Attributes. They are technically the same as custom taxonomies.
Note that product attributes can also be set on the individual product level. These custom attributes can also be used as facet source (as they are just custom fields). The code on this page works for both types of attributes.
Create single-select facets with attributes as data source
With the above product attributes in place, if you have created product variations based on them, you can now set up facets that use these attributes as their data source.
With these facets, products can be filtered by attribute. For example, if a user uses the “size” and “color” facets to filter by options “medium” and “red”, only products with the variation “medium + red” will be shown.
Important to note here is that for the code below to work, these attribute-based facets need to be “single-select”, because the code changes the product links based on the chosen facet options. If you have a multi-select facet (like a Checkboxes facets) you can have multiple options selected at the same time. However, variations cannot be based on multiple values of the same attribute. You cannot for example have a variation based on “medium” + “large”. So a direct link to a variation with both “medium” + “large” does not make sense.
Good single-select facet types to choose from in this context are for example Dropdown facets, Radio facets, or fSelect facets (with the “Multi-select” setting disabled).
Dynamically change the product links to variation links
Usually, you’ll have one or more links set up for each product to link to its product page. These links can be created in your custom WP_Query’s loop, in a page builder module, or in several ways in the Listing Builder. These product links will look like this:
/product/hoody/
With the code below, we will dynamically change these product links to link directly to the variation chosen in the facet(s), with the chosen variation highlighted/selected on the product page.
To work in WooCommerce, for a global attribute size
these links need to look like this:
/product/hoody/?attribute_pa_size=medium
And for a product-level attribute brand
, like this:
brand/product/hoody/?attribute_brand=nike
Notice that for product-level attributes, the URL variable needs to be without pa_
.
The code below will dynamically change these product links to product variation links, based on the selections that the user has made in the variation/attribute-based facets.
Part 1: Add facets source fields to the AJAX response
First, add the following helper hook to your (child) theme’s functions.php:
How to use custom PHP code?
PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info
<?php // Part one: Output facet sources in AJAX response add_filter( 'facetwp_render_output', function( $output, $params ) { $sources = []; foreach ( FWP()->facet->facets as $facet_name => $facet_data ) { if ( isset ( $facet_data['source'] ) ) { $sources[ $facet_name ] = $facet_data['source']; } } $output['settings']['facet_sources'] = $sources; return $output; }, 10, 2 );
This hook adds each facet’s data source field to the AJAX response. We need the facets source fields in the next part of the code:
Part 2: Dynamically change the product links to variation links
In the next part of the code, we assume a result listing that is made with the Listing Builder, with one or more elements in the post layout set up to link to the post/product URL.
We select all of these link elements with .facetwp-template .fwpl-item a
, assuming all link elements link to the product URL. You can adapt this selector to select any link element in your setup, whether it is built with the Listing Builder or not. Note that this example uses FacetWP’s built-in fUtil library which works similarly to jQuery for most basic functions.
We assume two facets: one named size_variations
with attribute size
as data source, and another named color_variations
with attribute color
as data source. Change the facets
array to contain your facets that have their data source set to a product attribute.
How to use custom PHP code?
PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info
<?php // Part two: Add product attributes URL variables to selected product links add_action( 'wp_head', function() { ?> <script> document.addEventListener('facetwp-loaded', function() { let linkElement = fUtil('.facetwp-template .fwpl-item a'); // Select all link elements that will get their link changed. This works in Listing Builder templates. let facets = ['size_variations', 'color_variations']; // Change the array to include your facets that have product attributes as source. let urlvars = []; facets.forEach(function(facet) { if (FWP.facets[facet] !== undefined && FWP.facets[facet].length) { let sourcename = FWP.settings.facet_sources[facet]; // Part 1, with the facetwp_render_output hook, is needed for this! // Accept 'attribute_pa_size', 'pa_size' and 'attribute_pa_size' to support global and product-level attributes if (sourcename.includes('pa_') || sourcename.includes('attribute_')) { let urlvar = sourcename.substring(sourcename.lastIndexOf("/") + 1); // Strip prefixes like tax/, cf/ // Change to 'attribute_pa...' if 'pa_...' for global attributes if (urlvar.indexOf('pa_') == 0) { urlvar = 'attribute_' + urlvar; } urlvars.push(urlvar + '=' + FWP.facets[facet][0]); // Select the first option of the facet, when multiple variations are selected, e.g. when size=medium+small, it takes medium. Use single-select facets for this reason. } } }); // Join the URL vars for all attribute selected in these facets, and add to all selected link elements if (urlvars.length) urlvars = '?' + urlvars.join('&'); linkElement.each(el => el.setAttribute('href', el.getAttribute('href') + urlvars)); }); </script> <?php });
This code works for global product attributes as well as for product-level attributes. For global product attributes, there are two possible data source field names: e.g. attribute_pa_size
and pa_size
, depending on which field you choose in the facet’s Data source dropdown. However, only attribute_pa_size
works in WooCommerce for the variation link URL variable, so the code above adds attribute_
if the second one is used.
For product-level attributes, the field name will be attribute_size
, which will work as such in the variation link’s URL variable.
Part 2 alternative: Optionally change link or button text
You may want to make it more clear to users what happens when clicking the variation links. Say you have a button element that says “More info”. With a few small additions to the code we can change this text to “Buy” or “Add to cart” when a user has filtered by attribute and the product links have changed to variation links.
The following alternative code changes the text of a Listing Builder’s Button element to “Buy” after filtering with any of the attribute-based facets. We select these Button elements with their class fwpl-btn
. Of course, you can change this selector to any other link or button element. Note that this example uses FacetWP’s built-in fUtil library which works similarly to jQuery for most basic functions.
How to use custom PHP code?
PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info
<?php // Part two: Add product attributes URL variables to selected product links // Also change Button element text to "Buy" after facet selection add_action( 'wp_head', function() { ?> <script> document.addEventListener('facetwp-loaded', function() { let linkElement = fUtil('.facetwp-template .fwpl-item a'); // Select all link elements that will get their link changed. This works in Listing Builder templates. let facets = ['size_variations', 'color_variations']; // Change the array to include your facets that have product attributes as source. let urlvars = []; // Optional part 1/3: The link/button to change text when above facets have an option selection let textchange = false; let linkbutton = fUtil('.facetwp-template .fwpl-btn'); // The link/button element to change the text of facets.forEach(function(facet) { if (FWP.facets[facet] !== undefined && FWP.facets[facet].length) { let sourcename = FWP.settings.facet_sources[facet]; // Part 1, with the facetwp_render_output hook, is needed for this! // Accept 'attribute_pa_size', 'pa_size' and 'attribute_pa_size' to support global and product-level attributes if (sourcename.includes('pa_') || sourcename.includes('attribute_')) { let urlvar = sourcename.substring(sourcename.lastIndexOf("/") + 1); // Strip prefixes like tax/, cf/ // Change to 'attribute_pa...' if 'pa_...' for global attributes if (urlvar.indexOf('pa_') == 0) { urlvar = 'attribute_' + urlvar; } urlvars.push(urlvar + '=' + FWP.facets[facet][0]); // Select the first option of the facet, when multiple variations are selected, e.g. when size=medium+small, it takes medium. Use single-select facets for this reason. // Optional part 2/3: Change link/button text, but only when one of the above facets has an option selected textchange = true; } } }); // Optional part 3/3: The button/link text when one of the above facets has an option selected if (textchange) { linkbutton.each(el => el.textContent = 'Buy'); } // Join the URL vars for all attribute selected in these facets, and add to all selected link elements if (urlvars.length) urlvars = '?' + urlvars.join('&'); linkElement.each(el => el.setAttribute('href', el.getAttribute('href') + urlvars)); }); </script> <?php });
Unfortunately, it is not possible to also add a selected variation to the cart immediately, with a dynamically created link like this. “Add to cart links” do exist, also for variations, but they require a product ID and a variation ID to work with variable products:
/product/hoody/?add-to-cart=918&variation_id=6424
This would require knowing all variation IDs of all possible (combinations of) attributes on the facet page. And to know if a combination of selected facet options results in an actually existing combination. This is not feasible.
It would be possible with non-variable (“simple”) products on facet pages without variations, but that is for another tutorial.