Skip to content

Wheel and Tire product variants on the search results and in the Verify Fitment widget

If a store has wheels and tires with variants, it is possible to display the variants as products in the search results after selecting a vehicle and check the variants for the selected vehicle.

1. Connect the WheelTireVariantsPlugin and add the wheel_tire_hierarchy_type field

Section titled “1. Connect the WheelTireVariantsPlugin and add the wheel_tire_hierarchy_type field”
Plugins:
- WheelTireUpsizeDownsizePlugin
Fields:
wheel_tire_hierarchy_type:
    Faceted: true
    Scope: Internal
    Visibility: Hide
protected override void ConvertVariant(Item item, Item varItem, ProductVariant variant, Product product, int varNumber, ref bool skip)
{
base.ConvertVariant(item, varItem, variant, product, varNumber, ref skip);
// call this again, the variant image is removed in ShopifyFitmentSearchItemConverter.ConvertVariant
ImageGraphQL variantImage = variant.Image ?? product.Images.FirstOrDefault()?.Image;
if (variantImage is not null)
{
AddImages(varItem, [variantImage]);
}
varItem.Add("stock", FieldValues.All);
if (varItem.Get<bool>("available"))
{
varItem.Add("stock", FieldValues.Available);
}
else
{
varItem["out_of_stock"] = true;
}
varItem["inventory_quantity"] = variant.InventoryQuantity;
if (product.TracksInventory is true && variant.InventoryQuantity is > 0 and not 9999)
{
varItem.Add("stock", FieldValues.InStockOnly);
}
}

3. Create Wheel and Tire variants (please, follow the comments)

Section titled “3. Create Wheel and Tire variants (please, follow the comments)”

Wheel variants

protected override void AddWheels(Item item, Product product, IList<Item> productVariants)
{
if (product.HasOnlyDefaultVariant)
{
item[WheelTireVariantsPlugin.HierarchyField] = WheelTireVariantsPlugin.HierarchyType.Universal;
// Add wheel attributes here
}
else
{
AddWheelVariants(item, productVariants);
}
if (Store.Schema.Sources.ContainsKey(WheelTireFitmentHelper.SourceName))
{
item["Fitment"] = FitmentFieldValues.VehicleSpecific;
}
}
private void AddWheelVariants(Item item, IList<Item> productVariants)
{
// Add needed fields as collections to item
item["wheel_diameter"] = new HashSet<string>();
List<Item> processedVariants = [];
foreach (Item variant in productVariants)
{
Item processedVariant = new(variant)
{
[WheelTireVariantsPlugin.HierarchyField] = WheelTireVariantsPlugin.HierarchyType.Variant,
// Add all needed item fields to the variant item
["title"] = $"{item.Get("title")} {variant.Get("title")}",
["vendor"] = item["vendor"],
["vendor_url"] = item["vendor_url"],
["published_at"] = item["published_at"],
["collection_ids"] = item["collection_ids"],
["category"] = item["category"],
["YMMText"] = item["YMMText"],
[FitmentHelper.FitmentType] = new List<FitmentTypes>() { FitmentTypes.Wheel }
};
// Add all variant attributes to the item
string diameter = "17";
variant.Add("wheel_diameter", diameter);
item.Add("wheel_diameter", diameter);
processedVariants.Add(processedVariant);
}
if (processedVariants.Count > 0)
{
item[WheelTireVariantsPlugin.HierarchyField] = WheelTireVariantsPlugin.HierarchyType.Product;
item["product_variants"] = processedVariants;
}
else
{
item[WheelTireVariantsPlugin.HierarchyField] = WheelTireVariantsPlugin.HierarchyType.Universal;
}
}

Tire variants

protected override void AddTires(Item item, Product product, IList<Item> productVariants)
{
if (product.HasOnlyDefaultVariant)
{
base.AddTires(item, product, productVariants);
item[WheelTireVariantsPlugin.HierarchyField] = WheelTireVariantsPlugin.HierarchyType.Universal;
}
else
{
AddTireVariants(item, productVariants);
}
}
private void AddTireVariants(Item item, IList<Item> productVariants)
{
// Add needed fields as collections to item
item["tire_width"] = new HashSet<string>();
List<Item> processedVariants = [];
foreach (Item variant in productVariants)
{
Item processedVariant = new(variant)
{
[WheelTireVariantsPlugin.HierarchyField] = WheelTireVariantsPlugin.HierarchyType.Variant,
// Add all needed item fields to the variant item
["title"] = $"{item.Get("title")} ({variant.Get("title")})",
["vendor"] = item["vendor"],
["handle"] = item["handle"],
["published_at"] = item["published_at"],
["collection_ids"] = item["collection_ids"],
["category"] = item["category"],
["YMMText"] = item["YMMText"],
["Fitment"] = item["Fitment"],
[FitmentHelper.FitmentType] = new List<FitmentTypes>() { FitmentTypes.Tire }
};
// Add all variant attributes to the item
string width = "30";
variant.Add("tire_width", width);
item.Add("tire_width", width);
processedVariants.Add(processedVariant);
}
if (processedVariants.Count > 0)
{
item[WheelTireVariantsPlugin.HierarchyField] = WheelTireVariantsPlugin.HierarchyType.Product;
item["product_variants"] = processedVariants;
}
else
{
item[WheelTireVariantsPlugin.HierarchyField] = WheelTireVariantsPlugin.HierarchyType.Universal;
}
}

4. Override the GetProducts method to add variants to the index

Section titled “4. Override the GetProducts method to add variants to the index”
public override async IAsyncEnumerable<IEnumerable<Item>> GetProducts()
{
await foreach (IEnumerable<Item> batch in base.GetProducts())
{
List<Item> products = [];
foreach (Item item in batch)
{
if (item.ContainsKey("product_variants"))
{
products.AddRange((List<Item>)item["product_variants"]);
item.Remove("product_variants");
}
products.Add(item);
}
yield return products;
}
}

5. Use variants for the Verify Fitment widget

Section titled “5. Use variants for the Verify Fitment widget”

Override the getItemId function and update the Verify Fitment widget on a variant selection

const getItemId = ({ getItemId, getVariantId }) => {
// check if the product has variants
// e.g. 'body.page-type-product variant-selects'
if (window.document.querySelector('PRODUCT OPTIONS SELECTOR')) {
const variantId = getVariantId();
if (variantId) {
return `${variantId}`;
}
}
return getItemId();
};
const InitFunc = () => {
// e.g. 'body.page-type-product variant-selects input'
const productOptions = window.document.querySelectorAll('OPTION INPUTS SELECTOR');
productOptions?.forEach((element) => {
element.addEventListener('change', () => {
setTimeout(() => {
window.Convermax.updateVerifyFitmentWidget(getItemId());
}, 300);
});
});
};
export default {
...
InitFunc,
product: {
...,
getItemId,
},
...
};