[Dev] Product Sample for WooCommerce

The Sample object

Product Sample for WooCommerce uses a Sample object class which extends the WooCommerce WC_Data class in the same way that the WC_Product class does. This way, every property and method can be inherited and, eventually, adjusted to describe the sample properties, relative to the regular product it relates to.

The main difference with a regular product is that a sample is not stored as a separate row in the wp_posts table. Instead, the information about the product sample is a set of metadata stored in the wp_postmeta table with the same ID as the regular product. For this reason, from the perspective of the interaction with the database, the Sample class doesn’t use a WC_Data_Store class. It rather defines its own readsaveupdate, or delete methods that take care of the retrieval, update and deletion of the sample-related metadata from the wp_postmeta table.

Still, the Sample class is designed to closely replicate the same properties and methods of the WC_Product class. This allows developers to extend the plugin using the knowledge they already have about the WC_Product class.

For example, consider the following code snippet:

global $product;
echo $product->get_price();
// output: 10

Similarly, the following code snippet does the same thing but with the sample price instead:

global $product;
$sample = new Barn2\Plugin\WC_Product_Sample\Sample( $product );
echo $sample->get_price();
// output: 1

(Note the top-level namespace will change to Kestrel in a future release, we will mirror this class for backwards compatibility, though, or announce a major change.)

In this code snippet, you can see that a sample object can be instantiated from the $product object it refers to. This can be done by passing either the $product object itself or its ID (although the former method, when viable, is preferred as it prevents an unnecessary call to the wc_get_product function). Then, every property and method can be accessed the same way developers are already used to with the $product object. The only difference is that the sample object will be returning the product sample properties instead of the regular product ones (when they are different, of course).

Whether the $sample object will return the same result the $product object does is just a matter of how the sample is configured. For example, if a sample is set to come from the same stock of the regular product, then all the inventory properties and methods (stock quantity, stock status, weight and dimensions) are going to return the exact same values the regular product does (and no additional, unnecessary data will be stored in the database).

The product property

Every sample can be instantiated from the product object it refers to. Because of the tight connection between a sample and the regular product it represents, a reference of the regular product is stored as a property of the sample object so that it can always be referred to when needed. This way, even for those properties or methods that are not replicated in the Sample class design, developers can always refer to the connected product and use its properties and methods instead.

Let’s consider the following example. A store owner wants to disable the possibility to order samples of all the products that are currently on sale. This can be easily done with the following code snippet:

add_filter( 'wsa_sample_should_add_button', function( $is_button_visible, $sample ) {
    $product = $sample->get_product();
    return $is_button_visible && ! $product->is_on_sale();
}, 10, 2 );

It is important to notice that, in the case of product variations, the product property of a sample refers to the variation. If it is necessary to reference the variable product that variation is a child of, it will be possible to use the parent_product property instead, which is going to be instantiated only if the current product is a variation.

Sample objects in actions and filters

Similar to what happens with properties and methods, the Sample class replicates also many of the action and filter hooks associated with a WC_Product class. For example, when retrieving a sample property, the value can be filtered using the wsa_sample_get_{$prop} filter hook the exact same way one can use the woocommerce_product_get_{$prop} filter hook for the same purpose. Let’s consider the following code snippet:

add_filter( 'wsa_sample_get_price', function( $price, $sample ) {
    if ( ! $sample->is_virtual() ) {
        $price += 2;
    }
    return $price;
}, 10, 2 );

The fragment above increases the price of samples of non-virtual products by 2 (e.g. $5 becomes $7). As you can see, the $sample object has the same is_virtual() conditional method a regular $product object does and we don’t need to refer to the connected product object.

Filters

wsa_sample_add_to_cart_description

Filter the description of the add to cart button. This is used as the aria-label attribute of the link element.

add_filter( 'wsa_sample_add_to_cart_description', 'my_wsa_sample_add_to_cart_description', 10, 2 );
function my_wsa_sample_add_to_cart_description( $description, $sample ) {
	// your code here...

	return $description;
}

wsa_sample_add_to_cart_text

Filter the text used for the sample button on the shop and product archive pages. If you want to customize the button used on single product pages, you have to use the wsa_sample_single_add_to_cart_text filter hook instead.

add_filter( 'wsa_sample_add_to_cart_text', 'my_wsa_sample_add_to_cart_text', 10, 2 );
function my_wsa_sample_add_to_cart_text( $button_text, $sample ) {
	if ( 0 === $sample->get_price() ) {
		$button_text = __( 'Get a free sample!', 'my-text-domain' );
	}

	return $button_text;
}

wsa_sample_add_to_cart_url

Filter the URL of the product associated with the current sample object

add_filter( 'wsa_sample_add_to_cart_url', 'my_wsa_sample_add_to_cart_url', 10, 2 );
function my_wsa_sample_add_to_cart_url( $permalink, $sample ) {
	// your code here...

	return $permalink;
}

wsa_sample_backorders_allowed

Filter the condition determining whether backorders are allowed or not

add_filter( 'wsa_sample_backorders_allowed', 'my_wsa_sample_backorders_allowed', 10, 2 );
function my_wsa_sample_backorders_allowed( $backorders_allowed, $sample ) {
	// your code here...

	return $backorders_allowed;
}

wsa_sample_backorders_require_notification

Filter the condition determining whether backorders require notifications or not

add_filter( 'wsa_sample_backorders_require_notification', 'my_wsa_sample_backorders_require_notification', 10, 2 );
function my_wsa_sample_backorders_require_notification( $require_notification, $sample ) {
	// your code here...

	return $require_notification;
}

wsa_sample_downloadable_file_permission

Filter the downloadable file permission, passing the WC_Customer_Download object, the Sample object, the WC_Order object, the quantity and the WC_Order_Item object that corresponds to the sample in the order.

add_filter( 'wsa_sample_downloadable_file_permission', 'my_wsa_sample_downloadable_file_permission', 10, 5 );
function my_wsa_sample_downloadable_file_permission( $download, $sample, $order, $qty, $item ) {
	// your code here...

	return $download;
}

wsa_sample_empty_price_html

Filter the output of an empty price

add_filter( 'wsa_sample_empty_price_html', 'my_wsa_sample_empty_price_html', 10, 2 );
function my_wsa_sample_empty_price_html( $price, $sample ) {
	// your code here...

	return $price;
}

wsa_sample_file

Filter the file corresponding to a download ID

add_filter( 'wsa_sample_file', 'my_wsa_sample_file', 10, 3 );
function my_wsa_sample_file( $file, $sample, $download_id ) {
	// your code here...

	return $file;
}

wsa_sample_file_download_path

Filter the file download path

add_filter( 'wsa_sample_file_download_path', 'my_wsa_sample_file_download_path', 10, 3 );
function my_wsa_sample_file_download_path( $file_path, $sample, $download_id ) {
	// your code here...

	return $file_path;
}

wsa_sample_get_{$prop}

Filter the value of a sample property. The variable portion of the hook can be replaced with any of the internal properties of the product sample class: status, price, tax_status, tax_class, use_product_stock, stock_status, manage_stock, stock, backorders, low_stock_amount, weight, length, width, height, shipping_class_id, downloads, download_limit, download_expiry.

add_filter( 'wsa_sample_get_price', 'my_wsa_sample_get_price', 10, 2 );
function my_wsa_sample_get_price( $price, $sample ) {
	// your code here...

	return $price;
}

wsa_sample_get_availability

Filter the availability of the current sample

add_filter( 'wsa_sample_get_availability', 'my_wsa_sample_get_availability', 10, 2 );
function my_wsa_sample_get_availability( $availability, $sample ) {
	// your code here...

	return $availability;
}

wsa_sample_get_availability_class

Filter the HTML class of the availability element

add_filter( 'wsa_sample_get_availability_class', 'my_wsa_sample_get_availability_class', 10, 2 );
function my_wsa_sample_get_availability_class( $class, $sample ) {
	// your code here...

	return $class;
}

wsa_sample_get_availability_text

Filter the availability label of the current sample

add_filter( 'wsa_sample_get_availability_text', 'my_wsa_sample_get_availability_text', 10, 2 );
function my_wsa_sample_get_availability_text( $availability, $sample ) {
	// your code here...

	return $availability;
}

wsa_sample_get_dimensions

Filter the dimensions of the product sample

add_filter( 'wsa_sample_get_dimensions', 'my_wsa_sample_get_dimensions', 10, 2 );
function my_wsa_sample_get_dimensions( $dimensions, $sample ) {
	// your code here...

	return $dimensions;
}

wsa_sample_get_image

Filter the markup of the image attached to the product sample

add_filter( 'wsa_sample_get_image', 'my_wsa_sample_get_image', 10, 4 );
function my_wsa_sample_get_image( $image, $sample, $size, $placeholder ) {
	// your code here...

	return $image;
}

wsa_sample_get_price_html

Filter the price HTML markup of the current sample object

add_filter( 'wsa_sample_get_price_html', 'my_wsa_sample_get_price_html', 10, 2 );
function my_wsa_sample_get_price_html( $price, $sample ) {
	// your code here...

	return $price;
}

wsa_sample_get_price_suffix

Filter the price suffix of the current sample

add_filter( 'wsa_sample_get_price_suffix', 'my_wsa_sample_get_price_suffix', 10, 4 );
function my_wsa_sample_get_price_suffix( $suffix, $sample, $price, $qty ) {
	// your code here...

	return $suffix;
}

wsa_sample_is_in_stock

Filter the stock status

add_filter( 'wsa_sample_is_in_stock', 'my_wsa_sample_is_in_stock', 10, 2 );
function my_wsa_sample_is_in_stock( $is_in_stock, $sample ) {
	// your code here...

	return $is_in_stock;
}

wsa_sample_is_taxable

Filter whether the sample is taxable or not

add_filter( 'wsa_sample_is_taxable', 'my_wsa_sample_is_taxable', 10, 2 );
function my_wsa_sample_is_taxable( $is_taxable, $sample ) {
	// your code here...

	return $is_taxable;
}

wsa_sample_loop_add_to_cart_args

Filter the arguments passed to the loop add to cart button

add_filter( 'wsa_sample_loop_add_to_cart_args', 'my_wsa_sample_loop_add_to_cart_args', 10, 2 );
function my_wsa_sample_loop_add_to_cart_args( $args, $sample ) {
	// your code here...

	return $args;
}

wsa_sample_loop_add_to_cart_hook

Filter the hook, priority and function (`add_action` or `add_filter`)
that must be used to print the sample button in the shop/archive pages

add_filter( 'wsa_sample_loop_add_to_cart_hook', 'my_wsa_sample_loop_add_to_cart_hook' );
function my_wsa_sample_loop_add_to_cart_hook( $args ) {
	// your code here...

	return $args;
}

wsa_sample_maximum_quantity_description

Filter the description of the maximum quantity field

add_filter( 'wsa_sample_maximum_quantity_description', 'my_wsa_sample_maximum_quantity_description' );
function my_wsa_sample_maximum_quantity_description( $description ) {
	// your code here...

	return $description;
}

wsa_sample_quantity_setting_attributes

Filter the HTML attributes of the quantity setting field

add_filter( 'wsa_sample_quantity_setting_attributes', 'my_wsa_sample_quantity_setting_attributes' );
function my_wsa_sample_quantity_setting_attributes( $attributes ) {
	// your code here...

	return $attributes;
}

wsa_sample_should_add_button

Filter the condition determining whether a sample button should be displayed or not

add_filter( 'wsa_sample_should_add_button', 'my_wsa_sample_should_add_button', 10, 2 );
function my_wsa_sample_should_add_button( $is_button_visible, $sample ) {
	// your code here...

	return $is_button_visible;
}

wsa_sample_single_add_to_cart_attributes

Filter the attributes of the button element

add_filter( 'wsa_sample_single_add_to_cart_attributes', 'my_wsa_sample_single_add_to_cart_attributes', 10, 2 );
function my_wsa_sample_single_add_to_cart_attributes( $attributes, $sample ) {
	// your code here...

	return $attributes;
}

wsa_sample_single_add_to_cart_text

Filter the text used for the sample button on the single product pages. If you want to customize the button on the shop and archive pages, you have to use the wsa_sample_add_to_cart_text filter hook instead.

add_filter( 'wsa_sample_single_add_to_cart_text', 'my_wsa_sample_single_add_to_cart_text', 10, 2 );
function my_wsa_sample_single_add_to_cart_text( $button_text, $sample ) {
	if ( 0 === $sample->get_price() ) {
		$button_text = __( 'Get a free sample!', 'my-text-domain' );
	}

	return $button_text;
}

wsa_sample_supports

Filter the file download path

add_filter( 'wsa_sample_supports', 'my_wsa_sample_supports', 10, 3 );
function my_wsa_sample_supports( $supported, $feature, $sample ) {
	// your code here...

	return $supported;
}

wsa_sample_update_sample_stock_query

Filter the SQL setting the stock quantity after the order

add_filter( 'wsa_sample_update_sample_stock_query', 'my_wsa_sample_update_sample_stock_query', 10, 4 );
function my_wsa_sample_update_sample_stock_query( $sql, $sample_id_with_stock, $new_stock, $operation ) {
	// your code here...

	return $sql;
}

Actions

wsa_sample_after_inventory_sample_data

add_action( 'wsa_sample_after_inventory_sample_data', 'my_wsa_sample_after_inventory_sample_data' );
function my_wsa_sample_after_inventory_sample_data() {
	// your code here...
}

wsa_sample_after_loop_add_to_cart

add_action( 'wsa_sample_after_loop_add_to_cart', 'my_wsa_sample_after_loop_add_to_cart' );
function my_wsa_sample_after_loop_add_to_cart() {
	// your code here...
}

wsa_sample_after_shipping_sample_data

add_action( 'wsa_sample_after_shipping_sample_data', 'my_wsa_sample_after_shipping_sample_data' );
function my_wsa_sample_after_shipping_sample_data() {
	// your code here...
}

wsa_sample_before_general_sample_data

add_action( 'wsa_sample_before_general_sample_data', 'my_wsa_sample_before_general_sample_data' );
function my_wsa_sample_before_general_sample_data() {
	// your code here...
}

wsa_sample_before_inventory_sample_data

add_action( 'wsa_sample_before_inventory_sample_data', 'my_wsa_sample_before_inventory_sample_data' );
function my_wsa_sample_before_inventory_sample_data() {
	// your code here...
}

wsa_sample_before_loop_add_to_cart

add_action( 'wsa_sample_before_loop_add_to_cart', 'my_wsa_sample_before_loop_add_to_cart' );
function my_wsa_sample_before_loop_add_to_cart() {
	// your code here...
}

wsa_sample_before_shipping_sample_data

add_action( 'wsa_sample_before_shipping_sample_data', 'my_wsa_sample_before_shipping_sample_data' );
function my_wsa_sample_before_shipping_sample_data() {
	// your code here...
}

wsa_sample_cat_add_after_sample_price

add_action( 'wsa_sample_cat_add_after_sample_price', 'my_wsa_sample_cat_add_after_sample_price' );
function my_wsa_sample_cat_add_after_sample_price() {
	// your code here...
}

wsa_sample_cat_edit_after_sample_price

add_action( 'wsa_sample_cat_edit_after_sample_price', 'my_wsa_sample_cat_edit_after_sample_price' );
function my_wsa_sample_cat_edit_after_sample_price() {
	// your code here...
}

wsa_sample_delete_product_data

Fire right after product sample data has been deleted from the database

add_action( 'wsa_sample_delete_product_data', 'my_wsa_sample_delete_product_data' );
function my_wsa_sample_delete_product_data( $id ) {
	// your code here...
}

wsa_sample_read_product_data

Fire right after product sample data has been read from the database

add_action( 'wsa_sample_read_product_data', 'my_wsa_sample_read_product_data' );
function my_wsa_sample_read_product_data( $id ) {
	// your code here...
}

wsa_sample_updated_sample_stock

Fire an action for this direct update so it can be detected by other plugins.

add_action( 'wsa_sample_updated_sample_stock', 'my_wsa_sample_updated_sample_stock' );
function my_wsa_sample_updated_sample_stock( $sample_id_with_stock ) {
	// your code here...
}
Was this article helpful?

Related Articles