WOOT - WooCommerce Active Products Tables

Customize the variations popup of a WooCommerce product table

The variations popup of a WooCommerce product table, the window that opens when a visitor clicks select [5] next to a variable product, is a small products table of its own. To customize the variations popup you change that table: its columns, rows per page, sort order, extra fields and look. Some of this is a setting and the rest takes a few lines of code. Each task is below, with the reason it works the way it does.

How the variations popup of a WooCommerce product table works

Two buttons open the popup. In the Cart column a variable product gets select [N] instead of an Add to cart button, and the Variations column shows Var.[N], where N is the number of variations. Both load the [woot_variations] table for that product.

  • The popup does not use the columns of the table it was opened from. Its columns come from WOOT → Settings → Variations columns; only the skin is taken over from the parent table.
  • All variations of the product are loaded at once, then paged and sorted in the browser: 10 rows per page, pagination on top, rows sorted alphabetically by the Title cell. A variation's title is the product name plus its attribute values, which is why sizes come out as L, M, S, XL.
  • The popup is always a table. Its layout cannot be replaced with a template, but every column in it can be changed or added.

Code changes go through one filter, woot_vurgc_shortcode_data. It runs for the variations, upsells, cross-sells, grouped and related tables right before the table is drawn and receives all its arguments: filter_provider is woot_variations for variations, and filter_data['product_id'] holds the product. Older snippets used woot_review_shortcode_args, which filters every [woot] table on the site; use the dedicated hook instead. All snippets below go into the functions.php of a child theme or into a code snippets plugin.

Customize the variations popup columns

Open WOOT → Settings, find Variations columns, and select the fields you want; press and hold a selected field to drag it into another position. The list applies to every variations popup and to every [woot_variations] shortcode without its own columns. The All possible columns keys button under the settings table lists the key of every field.

A variations table placed on a page can have its own columns: [woot_variations id=49 columns='thumbnail,pa_size,pa_color,price,add_to_cart'], where id is the product ID. On a single product page the ID can be left out, and the current product is used; see how to insert shortcodes into the single product page.

Two details save time. Attribute columns such as pa_size show the value of each variation, and the title gets the attribute values too, but only for global attributes created under Products → Attributes. Values of custom attributes typed directly into a product appear neither in the title nor as a column. And the default header of an attribute column is the WooCommerce taxonomy name, such as Product Size; rename it with the Vocabulary.

Rows per page and pagination

On a page, use the per_page attribute: [woot_variations id=49 per_page=4], or per_page=-1 to show all variations without pagination. For the popup, set the same arguments in the hook:

add_filter('woot_vurgc_shortcode_data', function ($args) {
    if (isset($args['filter_provider']) && $args['filter_provider'] === 'woot_variations') {
        $args['per_page'] = 20;           // -1 shows all variations
        $args['per_page_position'] = 'b'; // pagination: t, b, tb or none
    }
    return $args;
});

Change the sort order: by ID, by price, as in the product editor

The popup sorts rows in the browser by the contents of a cell. Sorting by ID works directly, because the ID cell holds a plain number:

add_filter('woot_vurgc_shortcode_data', function ($args) {
    if (isset($args['filter_provider']) && $args['filter_provider'] === 'woot_variations') {
        $args['orderby'] = 'id';
        $args['order'] = 'asc';
    }
    return $args;
});

The ID column must be among the Variations columns for this to work, since the rows only contain the selected fields; to keep it out of sight, remove it from $args['columns'] in the same hook, as the next example does with its helper column. Sorting by the Price column does not give the expected result: the price cell holds formatted HTML with the currency symbol, so the values are compared as text and 105 lands before 12, and 12 before 9. The reliable way is a helper column with the bare number, used for sorting and kept out of sight:

// 1. A helper column with the price as a plain number
add_filter('woot_profile_extend', function ($profile, $action_name) {
    if ($action_name === 'woot_woocommerce_tables') {
        $profile['sort_price'] = [
            'title'   => 'Sort: price',
            'display' => false, // not offered in the Columns tab of regular tables
            'action'  => function ($post_id) {
                $product = WOOT_WooCommerce::get_product($post_id);
                return is_object($product) ? (float) $product->get_price() : 0;
            },
        ];
    }
    return $profile;
}, 10, 2);

// 2. Sort the popup by it and hide the helper column
add_filter('woot_vurgc_shortcode_data', function ($args) {
    if (isset($args['filter_provider']) && $args['filter_provider'] === 'woot_variations') {
        $args['orderby'] = 'sort_price';
        $args['order'] = 'asc'; // 'desc' puts the most expensive first
        $args['columns'] = implode(',', array_diff(explode(',', $args['columns']), ['sort_price']));
    }
    return $args;
});

After adding the code, add Sort: price to Variations columns in Settings, otherwise the rows will not carry the number. For the order you set by dragging variations in the product editor, use the same pattern with a helper column that returns (int) get_post_field('menu_order', $post_id).

Show variation custom fields and parent product data

Fields added in the Meta tab belong to one table, and the popup has no table ID, so they never show up in Variations columns. Register the meta key for all tables instead, as described in how to add a meta key globally to all shortcodes:

add_filter('woot_profile_extend', function ($profile, $action_name) {
    if ($action_name === 'woot_woocommerce_tables') {
        $meta_key = 'msrp'; // the meta key of your variation field
        $profile[$meta_key] = woot()->columns->meta->assemble_meta_for_system([
            'title'     => WOOT_Vocabulary::get('MSRP'),
            'meta_key'  => $meta_key,
            'meta_type' => 'text', // text, number or calendar
        ]);
    }
    return $profile;
}, 10, 2);

The new column appears in Variations columns. In the popup each row reads the field from the variation itself, so a field saved per variation shows its own value in every row.

Data of the parent product works the same way, because a custom column receives the ID of the variation in that row. The built-in Thumbnail column shows only the variation's own image; this column shows the variation image or, when the variation has none, the product image, since WooCommerce falls back to the parent image for variations:

add_filter('woot_profile_extend', function ($profile, $action_name) {
    if ($action_name === 'woot_woocommerce_tables') {
        $profile['image_or_parent'] = [
            'title'  => WOOT_Vocabulary::get('Image'),
            'action' => function ($post_id) {
                $product = WOOT_WooCommerce::get_product($post_id);
                $image_id = is_object($product) ? $product->get_image_id() : 0;
                $src = $image_id ? wp_get_attachment_image_src($image_id, 'thumbnail') : false;
                return $src ? WOOT_HELPER::draw_html_item('img', ['src' => $src[0], 'width' => 60, 'alt' => '']) : '';
            },
        ];
    }
    return $profile;
}, 10, 2);

For anything else from the parent, $product->get_parent_id() gives its ID.

Different columns for different user roles

The hook runs for every popup request, so it can check who is looking. This version hides the SKU and stock columns from everyone except one role:

add_filter('woot_vurgc_shortcode_data', function ($args) {
    if (isset($args['filter_provider']) && $args['filter_provider'] === 'woot_variations') {
        $user = wp_get_current_user();
        if (!in_array('wholesale_customer', (array) $user->roles, true)) { // your role slug
            $args['columns'] = implode(',', array_diff(explode(',', $args['columns']), ['sku', 'stock_quantity']));
        }
    }
    return $args;
});

One rule applies: the rows are prepared before the hook runs, from the fields selected in Settings. So select in Variations columns every column that any role may see, in the right order, and only remove columns in the hook. A column added in the hook would stay empty, and a reordered list would put headers above the wrong cells.

Add to cart and styling

Each row has its own quantity field and Add to cart button. A variation without a price, or out of stock, gets no button: WooCommerce does not consider it purchasable, and the table follows that. A price of 0 is a valid price, so the button stays. To stop zero-priced variations from being bought anywhere in the shop, including the popup:

add_filter('woocommerce_variation_is_purchasable', function ($purchasable, $variation) {
    if ((float) $variation->get_price() <= 0) {
        return false;
    }
    return $purchasable;
}, 10, 2);

For styling, a variations table placed on a page has the same structure and classes as any other table, plus the class woot_variations_columns. Popup tables sit inside .woot-content-in-popup. Every cell has the class table23_td_ plus the field key, and every header has a data-key attribute. The Custom CSS tab of a table does not reach popups, which have no table ID, so put the rules into your theme's custom CSS field:

/* Variations table on a page */
.woot_variations_columns .table23_td_price {
    font-weight: 600;
}

/* Tables in popups: variations, upsells, grouped */
.woot-content-in-popup th[data-key="stock_quantity"],
.woot-content-in-popup .table23_td_stock_quantity {
    text-align: center;
}

The popup is requested from the server each time it opens, so changes in Settings and in code show up the next time a visitor clicks the button. Questions about a specific setup are welcome on the WOOT support forum.