I'am building a shop for a client.
Now on the product page, there is a dropdown where you can choose Delivery "standard" or "express.
When this is chosen by the customer you can choose the amount you want from this product.
now i found a piece of code on stackoverflow ( Woocommerce get variation product price ) to display the correct price directly after the amount in the dropdown. this works perfect.
But now , the price of the first amount variation (100 (€22) ) is also displayed on the delivery dropdown ( 2 - 3 days (€22) . And i would like to remove the price from the delivery variable / dropdown.
add_filter( 'woocommerce_variation_option_name', 'display_price_in_variation_option_name' );
function display_price_in_variation_option_name( $term ) {
global $wpdb, $product;
$result = $wpdb->get_col( "SELECT slug FROM {$wpdb->prefix}terms WHERE name = '$term'" );
$term_slug = ( !empty( $result ) ) ? $result[0] : $term;
$query = "SELECT postmeta.post_id AS product_id
FROM {$wpdb->prefix}postmeta AS postmeta
LEFT JOIN {$wpdb->prefix}posts AS products ON ( products.ID = postmeta.post_id )
WHERE postmeta.meta_key LIKE 'attribute_%'
AND postmeta.meta_value = '$term_slug'
AND products.post_parent = $product->id";
$variation_id = $wpdb->get_col( $query );
$parent = wp_get_post_parent_id( $variation_id[0] );
if ( $parent > 0 ) {
$_product = new WC_Product_Variation( $variation_id[0] );
return $term . ' (' . woocommerce_price( $_product->get_price() ) . ')';
}
return $term;
}
I tried to change the $variation_id[0]; to 1 , 2 , 3 ,4 and 5 , but no succes, so i assume there must be another way to fix it.
Thnx in advance :)
What you need to do is to put some logic in your display_price_in_variation_option_name() function to check whether the filter should apply to a particular attribute or not. One big problem in this case is that the only thing you have access to in your function is the name of the term, which is not guaranteed to be unique. For example, you could have a variation called "Blue" in both a "Material Color" or "Packaging Color" attribute. We need to find out exactly which attribute this term belongs to, and the only reasonable way to do that, as far as I can tell, is to modify the templates that apply this filter, and pass in an extra "taxonomy" parameter that you can check within the function.
For example, on line 48 of the template
/wp-content/plugins/woocommerce/templates/single-product/add-to-cart/variable.php
you will find two instances of the 'woocommerce_variation_option_name' filter, one on line 48 and one on line 54 (as of version 2.3.0). You will see that these currently pass in a single parameter. We need to pass in a second parameter, which is the name of the attribute the term belongs to. Luckily, that variable already exists in scope, and it's called $name. So, we need to make two modifications to the template. First, we should add the second $name variable, as such:
apply_filters( 'woocommerce_variation_option_name', $term->name )
Should become
apply_filters( 'woocommerce_variation_option_name', $term->name, $name )
You will also need to update your filter to accept a second argument. So, in your original code, this:
add_filter( 'woocommerce_variation_option_name', 'display_price_in_variation_option_name' )
Should become:
add_filter( 'woocommerce_variation_option_name', 'display_price_in_variation_option_name', 10, 2 )
And you will also need to update your filter function to accept a second argument. So this:
function display_price_in_variation_option_name( $term ) {
Should become:
function display_price_in_variation_option_name( $term, $name = null ) {
Now you are ready to add the logical check to see if the attribute is one that should display the price. Since you only indicated you want to hide it for the shipping attribute, that's what we'll use. First you will need to find the full name of the attribute you're looking to suppress. The quickest way to do this is to go into the WP Admin and go to the Products -> Attributes section and select the shipping attribute that you want to suppress. Then in the URL you will see ?taxonomy= followed by something beginning with "pa_" - you want the thing that starts with "pa_" (it stands for product attribute). For this example, let's say that it's "pa_shipping". Now, in your filter function, you'll do the following:
function display_price_in_variation_option_name( $term, $name = null ) {
if($name == 'pa_shipping') {
return $term;
}
global $wpdb, $product;
// the rest of your original code
I've tested this out and it seems to work pretty well. There is also a JavaScript option that I could describe, but that would not be as robust (though it would not require you to alter template files).
Related
I'm working on a child theme where I want to split variable product into 2 elements:
Regular variation form with a dropdown where customer gets to pick from a single attribute with multiple terms except for one.
A single form with just one specific term and its own add to cart button.
For example:
When selling clothes we create "tshirts" attribute and use terms to describe both the ones that have prints on them (print 1, print 2, print3 etc.) as well as a regular, plain tshirt (which has a special "plain-shirt" term) and unlike others - doesn't appear in the dropdown but is a standalone element.
Attached image
I've tried looking into different solutions and eventually settled down on editing variable.php themplate, duplicating the variations_form and using code from this thread: Hide specific product attribute terms on WooCommerce variable product dropdown to filter out necessary terms. However, since 'wc_dropdown_variation_attribute_options' fucntion and hooks are already inside the form I'm struggling with how should I go about this. It is probably an extremely wonky approach in my case so any help, ideas and suggestions will be greatly appreciated.
Edit: Found a working soultion by further editing variable.php template:
For the first form I added a filter before "woocommerce_before_variations_form" hook:
add_filter( 'woocommerce_dropdown_variation_attribute_options_args', 'sample_dropdown', 10, 1 );
function sample_dropdown( $args ) {
$taxonomy = 'pa_capacity';
$targeted_terms_names = array( "plain-shirt", "plain-tshirt" );
$terms_slugs = array_filter( array_map( 'sanitize_title', $targeted_terms_names ) );
if( $args['attribute'] === $taxonomy ) {
foreach( $args['options'] as $key => $option ){
if( ! in_array( $option , $terms_slugs ) ) {
unset($args['options'][$key]);
}
}
}
return $args;
}
and removed filer on "woocommerce_after_variations_form" hook.
Then for the second form I applied another filter with reversed array
if( in_array( $option , $terms_slugs )
Instead of
if( ! in_array( $option , $terms_slugs ) )
Its' still very untidy but at least it works.
I'm currently having an issue with WooCommerce that randomly a product attribute is or is not written to the database. Result is that in the order for some items I can't see the attributes. Hence, I thought about having an intermediate solution by dumping the cart into an email to ourselves as soon as the customer hits the "order now" button.
But doing so, I'm struggling to get the attributes right.
We're selling coffee for which I have 2 attributes: coffee_bag_size and coffee_ground_for. Both attributes are set for variations, but I have created 2 only variations based on coffee_bag_size (for 250 grammes and 500 grammes bag) with different prices whereas the coffee_ground_for can be any value. I just have to know what the customer chooses.
I read in another post that in case an attribute is not used in variations, one has to get it from the parent, so I did:
foreach ( WC()->cart->get_cart() as $cart_item )
{
$product = $cart_item['data'];
$bagsize = $product->get_attribute('pa_coffee_bag_size') ;
// For Product Variation type
if( $product->get_variation_id() > 0 )
{
$parent = wc_get_product($product->get_parent_id());
$ground = $parent->get_attribute('pa_coffee_ground_for') ;
}
// For other Product types
else
{
$ground = $product->get_attribute('pa_coffee_ground_for') ;
}
}
Getting the coffee_bag_size attribute is no problem.
Getting the coffee_ground_for is the problem. If I get it from the product it's empty, if I get it from the parent then I get a comma-separated list of all possible values. But I only need the value that was chosen. How do I do that?
I tried a few things more .... they all give me an empty string back:
$ground = $product->get_meta('pa_coffee_ground_for',true);
$ground = $cart_item['variation']['pa_coffee_ground_for'];
$attributes = $product->get_attributes();
foreach ( $attributes as $attribute => $attribute_term )
{
$term = get_term_by('name','pa_coffee_ground_for', $attribute);
$ground = $term->term;
}
Check out slug of you attribute it may be 'pa_coffee-ground-for'
$product = $cart_item['data'];
if( $product->is_type( 'variation' ) )
{
$product = wc_get_product($product->get_parent_id());
}
$ground = $product->get_attribute( 'pa_coffee-ground-for' );
Tested and working
I would like on my site to be able to order the products page (woocommerce)
as well as with the classic settings
sort by popularity
sort by average rating
sort by newness
sort by price: low to high
sort by price: high to low
but also with an attribute present in all products
sort by my custom attribute: low to high
sort by my custom attribute: high to low
I found various posts about it and made several tests ... but I'm not a programmer ... I still haven't been able to solve the problem.
I found this article
https://docs.woocommerce.com/document/custom-sorting-options-ascdesc/
explaining how to add
sort by random
and it works correctly
but I didn't understand, starting from this code what you need to add / modify to replace random with my custom attribute.
My attribute name is "pa_kj"
Its value is alphanumeric
I thank everyone in advance for any suggestions
I found this code on the net
https://gist.github.com/bekarice/0df2b2d54d6ac8076f84
<?php
/**
* Adds WooCommerce catalog sorting options using postmeta, such as custom fields
* Tutorial: http://www.skyverge.com/blog/sort-woocommerce-products-custom-fields/
**/
function skyverge_add_postmeta_ordering_args( $sort_args ) {
$orderby_value = isset( $_GET['orderby'] ) ? wc_clean( $_GET['orderby'] ) : apply_filters( 'woocommerce_default_catalog_orderby', get_option( 'woocommerce_default_catalog_orderby' ) );
switch( $orderby_value ) {
// Name your sortby key whatever you'd like; must correspond to the $sortby in the next function
case 'location':
$sort_args['orderby'] = 'meta_value';
// Sort by meta_value because we're using alphabetic sorting
$sort_args['order'] = 'asc';
$sort_args['meta_key'] = 'location';
// use the meta key you've set for your custom field, i.e., something like "location" or "_wholesale_price"
break;
case 'points_awarded':
$sort_args['orderby'] = 'meta_value_num';
// We use meta_value_num here because points are a number and we want to sort in numerical order
$sort_args['order'] = 'desc';
$sort_args['meta_key'] = 'points';
break;
}
return $sort_args;
}
add_filter( 'woocommerce_get_catalog_ordering_args', 'skyverge_add_postmeta_ordering_args' );
// Add these new sorting arguments to the sortby options on the frontend
function skyverge_add_new_postmeta_orderby( $sortby ) {
// Adjust the text as desired
$sortby['location'] = __( 'Sort by location', 'woocommerce' );
$sortby['points_awarded'] = __( 'Sort by points for purchase', 'woocommerce' );
return $sortby;
}
add_filter( 'woocommerce_default_catalog_orderby_options', 'skyverge_add_new_postmeta_orderby' );
add_filter( 'woocommerce_catalog_orderby', 'skyverge_add_new_postmeta_orderby' );
but it doesn't work with the latest version of Woocommerce, it always returns the page with no products found.
Is there anyone experienced who can recommend the code to be modified?
Thanks again
Have Woocommerce setup with a range of variable products. In the variable tab I've setup a unique price, image, description, weight & dimensions for each item.
All variable data displays as expected on the front-end except the dimensions & weight.
Despite hours of searching, I cannot find any documentation, tutorials, hints on how to hook into it.
Have Woocommerce templates setup and know that I will need to hook into the do_action( 'woocommerce_single_variation' ); in variable.php.
Anyone know how to get each variable's dimensions & weight to display beneath the variable description?
If you have the variation ID, you can use it to create a new WC_Product(). This object will then have properties available on it for the $length, $width, and $height. See the docs here (at the bottom under "Magic Properties").
To get the variations for a given product, you can use the global $product and then the get_available_variations() function.
global $product
$variations = $product->get_available_variations();
foreach ( $variations as $variable_array ){
$variation = new WC_Product( $variable_array['variation_id'] );
echo "The length is {$variation->length}.";
}
If you want to display additional information regarding your variable product add this function to your child theme’s function.php (or plugin). You’ll probably want to alter the html tags to fit your theme:
add_filter( 'woocommerce_product_additional_information', 'tim_additional_tab', 9 );
function tim_additional_tab( $product ){
$variations = $product->get_available_variations();
//print the whole array in additional tab and examine it
//echo '<pre>';
//print_r($variations);
//echo '</pre>';
//html and style to your likings
foreach ( $variations as $key ){
echo $key['image']['title'].'<br>';
echo $key['weight_html'].'<br>';
echo $key['dimensions_html'].'<br>';
}
}
I am trying to edit the short-description template to be different on variable (single) product pages than on simple products. the code in that page is here:
global $post;
if ( ! $post->post_excerpt )
return;
?>
<div itemprop="description">
<?php echo apply_filters( 'woocommerce_short_description', $post->post_excerpt ) ?>
</div>
I want to add some code to the if statement that will be something like
if post has variations, don't display short description, if simple product DO display
but I can't find any way in the code to distinguish between a regular simple product post and one that is variable (has variations). And looking through the API docs over at the Woo site (http://docs.woothemes.com/wc-apidocs/) I found nothing of that sort.
Use $product->is_type() function to check the product type. To check if the product is a variable product use:
global $product;
// $product->is_type( $type ) checks the product type, string/array $type ( 'simple', 'grouped', 'variable', 'external' ), returns boolean
if ( $product->is_type( 'variable' ) ) {}
There is also $product->get_type() function that returns the internal type of a product as a string.
After much heartache, I have found the following two solutions:
In the product loop, you can use this:
if( $product->has_child() ) {
but for some reason in the short description on the single product page, I had to use this:
global $post;
$children = get_pages('child_of='.$post->ID);
if( count( $children ) !== 0 ) {
Hope this helps others that were struggling as I was...
Variable product always is based on WC_Product_Variable class. E.g. WooCommerce Subscriptions follows this approach.
So, the checking can be:
is_a( $product, 'WC_Product_Variable' )
This ensures that the type of product is variable regardless of the presence of children. And it's fast.
For some reason if you have deleted your variation, the has_child() function it still turns true.
So I used the below solution
if(empty($product->get_available_variations())) {
// Your code goes here
}
This is the solution for me, using on body_class filter:
if ( is_singular( 'product' ) ) {
$product = wc_get_product( get_the_ID() );
if ( $product->is_type( 'variable' ) ) {
...
}
}