I state that I am not an expert! My function modifies the visibility of the product based on the presence of the price. It works well with simple products, but not with products that have variables. To be more precise, the visibility of the variable is changed in the variable product, but not the parent product.
My question is: how do I, in case of variables, set the visibility filter on the parent product? This is my current code:
if ($listinoWeb == "")
{
delete_post_meta($product_id, 'wwpp_product_wholesale_visibility_filter', 'all');
add_post_meta($product_id, 'wwpp_product_wholesale_visibility_filter', 'wholesale_customer', TRUE);
}
else
{
add_post_meta($product_id, 'wwpp_product_wholesale_visibility_filter', 'all', TRUE);
delete_post_meta($product_id, 'wwpp_product_wholesale_visibility_filter', 'wholesale_customer');
}
You can call the get_parent_id() function on the product object. Which will return 0 if you call it on a simple/parent product. So you can do something like this:
$product_id = $product->get_parent_id() ? $product->get_parent_id() : $product->get_id();
This way you're sure the $product_id variable always contains the parent id.
Related
When I edit a product on woocommerce I'd like to have the default settin woocommerce visibility options radio button, not "hidden" but "visible".
I tried to use add_filter to set default value on "visible" like this:
add_filter( 'woocommerce_product_visibility_options', 'set_visibility_default_visible', 10, 1);
function set_visibility_default_visible($array) {
//set_catalog_visibility('visible');
return 'visible';
}
but I miss all the other options:
Any suggest, thanks.
product visibility default
If I understand what you are asking, you wish the "Catalogue visibility" panel to be open by default when editing a product, so that you can see the form. You can do this by adding some css to admin ie
add_action('admin_head', 'show_catalog_visibility');
function show_catalog_visibility() {
echo '<style>
#catalog-visibility-select{
display: block;
}
</style>';
}
The woocommerce_product_visibility_options filter is used to customise the actual options available not to determine if the panel is visible or not.
Edit : given the actual problem is that the product visibility for new products is defaulting to 'hidden' which is non standard
WooCommerce by default sets new product catalog visibility to 'visible'. If a new product is not starting out this way, it suggests that something eg a plugin is overriding the default value. Obviously it would always be best to identify the source of this behaviour if possible. In the worst case if the cause can't be found, the following code will set the default catalog visibility to 'visible' when a new product is created.
add_action('transition_post_status', 'jt_override_default_product_visibility', 10, 3);
function jt_override_default_product_visibility( $new_status, $old_status, $post ) {
if ( $old_status == 'new' && $new_status == 'auto-draft' && $post->post_type == 'product' ){
$_product = wc_get_product( $post->ID );
$_product->set_catalog_visibility('visible');
$_product->save();
}
}
Existing products visibility can be updated via a bulk edit from the product list page.
I have been looking EVERYWHERE and I cannot seem to find a solution to a very simple problem. I have a Woocommerce store and I want to show to the user a message "Out of stock" next to the variation if it is out of stock. I do not want it grayed out, I want the user to be able to select it so they can view the price if they wish. Most snippets I find online only works when they product has ONE variation but I need it to work for at least two. My website sells mobile devices so for example:
If a user selects the storage as 64GB then the color list updates showing which colors are out of stock in 64GB. If all colors are out of stock then next to 64GB it shows "Out of stock" also.
If the color is selected first, then the storage - the color list is then updated with what is out of stock.
Can anyone help me out here? Going insane trying to get this working. The below code works to gray out out of stock products in the exact way I mentioned above (it works for multiple products) but I can't figure out how to modify it for the use case mentioned above.
add_filter( 'woocommerce_variation_is_active', 'grey_out_variations_when_out_of_stock', 10, 2 );
function grey_out_variations_when_out_of_stock( $grey_out, $variation ){
if ( ! $variation->is_in_stock() ){
return false;
}else{
print_r($option_class);
return $term_name . ' - Out of Stock';;
}
}
You can check the availability of a product variation only if all but one of the attributes have been selected, not before.
All the necessary data is present on the product page and therefore you can use a jQuery script to process it.
In the variation form there is the data-product_variations attribute which contains an array with all the details of the product variations (variation id, stock status, attributes it uses, etc ...).
You can then make a comparison between the selected attributes and possible product variations.
The idea is this:
Check that there is more than one attribute (dropdown) on the product page
If all attributes (dropdowns) have been selected except one, get the attribute slug and the attribute value of the selected options
It compares them with the attributes that each single variation uses and, if the selected attributes are the same, gets the stock status of the last attribute to be selected
Adds the text "Out of stock" to each option whose product variation is not in stock. To do this it will use the woocommerce_update_variation_values event which fires after updating the options via Ajax (otherwise the changes will be overwritten)
The following code will work for 2 or more attributes (dropdowns) in
the variable product page.
// add the text "Out of stock" to each option of the last unselected attribute dropdown
add_action( 'wp_footer', 'add_out_of_stock_text_to_the_last_unselected_attribute_dropdown' );
function add_out_of_stock_text_to_the_last_unselected_attribute_dropdown() {
?>
<script type="text/javascript">
// initializes a global variable that will contain the attribute values to be updated ("Out of stock")
let globalAttributesToUpdate;
jQuery(function($){
// check if only one attribute is missing to be selected
function isLastAttribute(){
// if there is only one dropdown it returns false
if ( $('form.variations_form select').length == 1 ) {
return false;
}
// counts all selected attributes (excluding "Choose an option")
let count = 0;
$('form.variations_form select').each(function(){
if ( $(this).find(':selected').val() ) {
count++;
}
});
// if an attribute has not yet been selected, it returns true
if ( $('form.variations_form select').length - count == 1 ) {
return true;
} else {
return false;
}
}
$('form.variations_form select').change(function() {
if ( isLastAttribute() ) {
// clear the global variable every time
globalAttributesToUpdate = [];
let attrToFind = {}; // contains an object with slug and value of the selected attributes
let attrToSet; // contains the slug of the attribute not yet selected
$('form.variations_form select').each(function(index,object){
if ( $(this).find(":selected").val() ) {
attrToFind[$(this).data('attribute_name')] = $(this).find(":selected").val();
} else {
attrToSet = $(this).data('attribute_name');
}
});
// gets the value of the "data-product_variations" attribute of the variations form
let variationData = $('form.variations_form').data("product_variations");
$(variationData).each(function(index,object) {
let attrVariation = object.attributes;
let attrVariationLenght = Object.keys(attrVariation).length;
let found = 0;
let toSet;
let valueToSet;
// check all possible combinations of attributes (for single variation)
// based on the selected attributes
$.each( attrVariation, function( attr, value ) {
if ( attr in attrToFind && attrToFind[attr] == value ) {
found++;
} else {
// if the variation is out of stock it gets slug and value to add the text "Out of stock"
if ( object.is_in_stock == false ) {
toSet = attr;
valueToSet = value;
}
}
});
// if only one attribute is missing
if ( attrVariationLenght - found == 1 ) {
if ( toSet == attrToSet ) {
let obj = {};
obj[toSet] = valueToSet;
globalAttributesToUpdate.push(obj);
}
}
});
}
});
// inserts the text "Out of stock" after updating the variations
// based on the "globalAttributesToUpdate" global variable
$('body').on('woocommerce_update_variation_values', function(){
if ( globalAttributesToUpdate !== undefined && globalAttributesToUpdate.length ) {
$.each( globalAttributesToUpdate, function( key, attribute ) {
$.each( attribute, function( attrName, attrValue ) {
$('select[name='+attrName+'] > option[value="'+attrValue+'"]').append( " (Out of stock)" );
});
});
}
});
});
</script>
<?php
}
The code has been tested and works. Add it to your active theme's functions.php.
RESULT
RELATED ANSWERS
How to add variation stock status to Woocommerce product variation dropdown
Show stock status next to each attribute value in WooCommerce variable products
Display variation stock status on single dropdown variable products in Wocommerce 3
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]);
$_currency = get_woocommerce_currency_symbol();
$stock = $_product->is_in_stock() ? 'instock' : 'out of stock';
return $term . ' (' . $_product->get_price() . ' ' . $_currency . ') - ' . $stock;
}
return $term;
}
I have a woocommerce store and I’m importing custom fields like “color”, “length”, etc (around 10 fields) but not all product have values for these fields. Is there a way to hide the whole element of the value is empty or null. On the front end the elements show like this:
Length: 10cm
Height: 20cm
So basically if there is no length for a specific product I want to hide it.
Link to see: (check product extra details. In this example I only want to show the diameter and hide the rest since they are empty)
https://qadeemarabia.com/product/3d-bird-cookie-jar-off-white-matt/
You must use the woocommerce_display_product_attributes filter to delete empty items. My hypothesis for the empty condition is that the value is zero. You can change the condition according to what you need:
add_filter( 'woocommerce_display_product_attributes', 'ywp_hide_ptoperties_with_empty_values', 10, 2 );
function ywp_hide_ptoperties_with_empty_values( $attributes, $product ) {
// Loop to check values
foreach( $attributes as $attr ) {
$new_attr = array();
// Your condition for check value is empty
// You can use 'trim' etc.
$value = floatval( $attr['value'] );
// Check value is 0 here means value is empty
// and empty value skipped
if( $value != 0 ) {
$new_attr[] = $attr;
}
}
return $new_attr;
}
This code goes into the functions.php file of your active theme/child theme. Tested and works.
I'd like to display product variations for some (not all) variable products on the shop and product-category pages based on a custom field I'm planning to add to each variable product. To keep pagination working, I can hook in to the woocommerce_product_query hook in WC_Query::product_query($q) but I can't figure out how to modify the query to give me what I'm after.
The logic I want to apply is:
//Pseudo-code
$loop = $query->get_all_products // i.e. not just for one page
foreach ( $loop as $product) {
if ($product->is_simple) {
display_simple($product)
} else {
$variations = wc_get_products($product->get_children());
foreach ($variations as $product) {
if ($product->custom_show_on_archive_pages) {
display_variation($product)
}
}
}
}
The problem with the above (apart from being pseudo-code) is that it assumes we have an array called $loop but actually we have a query object.
My ideal would be to modify the query so that it generates the results as per the pseudo-code, and then my archive-product.php template can use an if/else to display simple vs variable products.
Something like this in my functions.php:
add_action( 'woocommerce_product_query', 'show_variable_products');
function show_variable_products( $q ) {
$meta_query = $q->get('meta_query');
// Do something
$q->set('meta_query', $meta_query);
}
I'm wondering if I need to actually run multiple queries somehow:
The original query but filtered to parent products
The children of these parent products (i.e. the variations), filtered to those with custom field checked
The original query but filtered to simple products
Merge (2) and (3)
Would this be possible?
Alternatively, is there another solution (that does not involve buying a plugin)?
So I'm trying to hide certain ship methods in Woocommerce based on a product tag. The main problem I face is my own lack PHP knowledge so I frankensteined the following code together with the help of some very friendly folks:
add_filter( 'woocommerce_available_shipping_methods', 'hide_shipping_based_on_tag' , 10, 1 );
function check_cart_for_share() {
// load the contents of the cart into an array.
global $woocommerce;
$cart = $woocommerce->cart->cart_contents;
$found = false;
// loop through the array looking for the tag you set. Switch to true if the tag is found.
foreach ($cart as $array_item) {
if (isset($array_item['product_tag']) && $array_item['product_tag'] == "CHOSEN_TAG") { // Replace "CHOSEN_TAG" with what ever tag you want
$found = true;
break;
}
}
return $found;
}
function hide_shipping_based_on_tag( $available_methods ) {
// use the function abve to check the cart for the tag.
if ( check_cart_for_share() ) {
// remove the rate you want
unset( $available_methods['flat_rate'] ); // Replace "flar_rate" with the shipping option that yu want to remove.
}
// return the available methods without the one you unset.
return $available_methods;
}
I understand that this code is by no means universal and thus the variables will be different from case to case but perhaps someone can tell me if something looks off in the code. Much appreciated
No doubt you have sorted this by now but your code was a good start for me ... and since I sorted it I have published it below. Your problem was that woocommerce doesn't have the product_tag in the cart array so you have to go and get it.
/* !Hide Shipping Options Woocommerce */
add_filter( 'woocommerce_available_shipping_methods', 'hide_shipping_based_on_tag' , 10, 1 );
function check_cart_for_share() {
// load the contents of the cart into an array.
global $woocommerce;
$cart = $woocommerce->cart->cart_contents;
$found = false;
// loop through the array looking for the tag you set. Switch to true if the tag is found.
foreach ($cart as $array_item) {
$term_list = wp_get_post_terms( $array_item['product_id'], 'product_tag', array( "fields" => "names" ) );
if (in_array("Heavy",$term_list)) { // Replace "Heavy" with what ever tag you want
$found = true;
break;
}
}
return $found;
}
function hide_shipping_based_on_tag( $available_methods ) {
// use the function above to check the cart for the tag.
if ( check_cart_for_share() ) {
// remove the rate you want
unset( $available_methods['flat_rate'] ); // Replace "flat_rate" with the shipping option that you want to remove.
}
// return the available methods without the one you unset.
return $available_methods;
}
You can use shipping class of Woocommerce to achieve this.
Here is the step by step instructions.
Create a shipping class (woocommerce->settings->shipping->shipping classes).
Associate this shipping class with products (that you wand to hide shipping methods for)
Use this snippet. (copy and pate to function.php).
For more information about the snippet is available here.