Separate attribute term from variable dropdown on single product page - woocommerce

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.

Related

How to use "has_term" correctly

Im working with the single product page and I need to have a different image (depending on the category) after the add to cart button.
I tried this code but it doesn't show me the image that I need
add_action ( 'woocommerce_after_add_to_cart_button', 'content_after_button' );
function content_after_button() {
if (has_term( 'Categoria', 'Accesorios' ) ) {
echo 'https://prueba.soygorrion.com.ar/wp-content/uploads/2019/08/iconos2.jpg';
}
I think im using has_term in the wrong way.
What im trying to accomplish is:
I have a parent category that is "Accesorios" and inside that I have other child categories like "Billeteras". For each one of this child categories it has to show a diferent image.
thank you
First of all there is issue with your has_term checking. If you check has_term docs, you can find it takes first parameter as category term and second parameter as taxonomy.
Add secondly you are just echo image url that is not going to display image.
So, do as follows -
add_action ( 'woocommerce_after_add_to_cart_button', 'content_after_button' );
function content_after_button() {
// do check with has_term( $term = '', $taxonomy = '', $post = null )
if( has_term( 'Accesorios', 'product_cat' ) ) {
echo '<img src="https://prueba.soygorrion.com.ar/wp-content/uploads/2019/08/iconos2.jpg" />';
}
}
Since you mentioned that the problem is with has_term function. If you can get the product ID inside add to cart, then you can use this code to get categories and check them:
$categories = wp_get_post_terms($product_id, 'product_cat');
if(in_array("Accesorios", $categories))
{
echo "bla bla";
}
I have tested this code for another purpose, it works fine. I hope this will help. Please inform me when you test it and if you face any errors update your question with your new code.

WooCommerce Undefined Property stdClass::$url

When i am installing woocommerce and logged out from my site then on home page woocommerce causes this problem.My wp-config.php is turned true , that is why its showing this error. but i need to know what is happening
Notice: Undefined property: stdClass::$url in /home/......./wp-content/plugins/woocommerce/includes/wc-page-functions.php on line 123
Line 123 of that file is within the following piece of code:
function wc_nav_menu_items( $items ) {
if ( ! is_user_logged_in() ) {
$customer_logout = get_option( 'woocommerce_logout_endpoint', 'customer-logout' );
if ( ! empty( $customer_logout ) ) {
foreach ( $items as $key => $item ) {
$path = parse_url( $item->url, PHP_URL_PATH );
$query = parse_url( $item->url, PHP_URL_QUERY );
if ( strstr( $path, $customer_logout ) || strstr( $query, $customer_logout ) ) {
unset( $items[ $key ] );
}
}
}
}
return $items;
}
add_filter( 'wp_nav_menu_objects', 'wc_nav_menu_items', 10 );
Notice on the last line there is a filter hook for wp_nav_menu_objects. So this function is used to filter the nav menu, and specifically looks like it is there solely so that WooCommerce can hide a logout menu object when the user is not logged in.
Now to the reason why the error is being thrown. I believe it stems back to the wordpress hook wp_nav_menu_objects. When looking at the function again it looks like the argument getting filtered named $items is expected to be a keyed array of stdClass instances, however on line 123 PHP is discovering that the url property is not available. This leads me to believe that another filter that occurs before this one is incorrectly modifying the $items array so that this filter can not correctly read the property.
Looking further in the file wc-page-functions.php on line 185 there is another hook for this filter:
add_filter( 'wp_nav_menu_objects', 'wc_nav_menu_item_classes', 2 );
However checking wc_nav_menu_item_classes reveals that it does not do anything special either. So here are my suggestions based on what has been presented:
Make sure that WordPress is up to date, the version of WooCommerce you are running maybe incompatible.
If you have code that filters wp_nav_menu_objects, make sure that you have not introduced a bug that changes the $items array to no longer contain instances of stdClass.
Try disabling/enabling 3rd party plugins that are not WooCommerce, you may find that one of these if out of date or just bugged.
Best of luck!
Koda

How to list post of certain category (only one category lets say cat id = 5 ) only on blog listing page?

I need to list posts of certain category only on blog listing page , let's say of category id 5. I need it to do from plugins or function.php file. I do not want to change the template files, i think blog listing is on index.php.
I used parse_query hook like following. But is affect other places as well . The menu bar is gone. Please help me. Thank you.
add_filter( 'parse_query', 'pp_posts_filter' );
function pp_posts_filter( $query ){
$query->query_vars['cat'] = 5;
}
To make the query changes specific to main query only and not secondary ones like Menus or Sidebars etc. Use is_main_query function i.e.
add_action( 'pre_get_posts', 'foo_modify_query_exclude_category' );
function foo_modify_query_exclude_category( $query ) {
if ( ! is_admin() && $query->is_main_query() && ! $query->get( 'cat' ) )
$query->set( 'cat', '-5' );
}

Woocommerce shows price on both variations

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).

Woocommerce - how to tell if product post has variations or not

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' ) ) {
...
}
}

Resources