Overwriting Woocommerce product attribute on save - wordpress

I am trying to alter an attribute when the user saves a new product or updates an existing. If the product is a certain category the 'weight' should be N/A..
Here is my product as it should start which is fine. Now I want to add a weight field to it (Which already exists as an attribute) and assign the weight to N/A. Here's the end result I want to do programmatically.
Here is as far as I can get:
add_action( 'woocommerce_update_product', 'mp_sync_on_product_save', 10, 1 );
function mp_sync_on_product_save( $product_id ) {
$product = wc_get_product( $product_id );
if($product->category_ids[0] == 18) {
//We are jewellery
$post_id = $product->get_id();
$product_attr = get_post_meta($post_id, '_product_attributes',true);//get the whole product attributes first
$product_attr['pa_weight'] = array(
'name' => 'pa_weight',
'value' => 'N/A',
'position' => 1,
'is_visible' => 1,
'is_variation' => 1,
'is_taxonomy' => '1'
);
update_post_meta($post_id , '_product_attributes',$product_attr);
}
}
Currently nothing seems to happen here. I hope I'm close so any help to get this over the line would be awesome.
Thanks

Related

Add attribute value automatically based on user meta key

I have created a registration form (using plugin ultimate-member) for a partner as a salesperson. The key here is:
Label : Registration Number
Meta : 'regno'
Value : X123W
So, each new registrant will get a different value as a salesperson unique ID .
On the other hand, I have created a product which is divided into 3 packages, separated by attribute with values ​​'pa_silver', 'pa_gold', and 'pa_diamond'.
'pa_silver' & 'pa_gold' => self-sold product (no salesperson required)
'pa_diamond' => product entrusted for selling (using salesperson).
Then on products that have taxonomy 'pa_diamond' I've added the attribute:
Label : List
tax : 'pa_list'
value : 'X123W', 'X234W' => this has been created manually.
Targets : What I want is that every time someone registers as a partner, the partner code should be "added" automatically on existing attribute ('pa_list') and on products that have taxonomy 'pa_diamond'.
I'm trying something, like:
$users = get_users( array( 'role' => array( 'partner' ) ) );
foreach($users as $user){
$regno = get_the_author_meta( 'regno', $user->ID);
echo $regno;
}
Note : it output like this => X345WX456W (2 New partner code)
Tried using the code I got from #LoicTheAztec's answer here.
The code is :
$product_id = get_the_ID();
$taxonomy = 'pa_list';
$clean_keywords = array('X345W','X456W');
$term_taxonomy_ids = wp_set_object_terms( $product_id, $clean_keywords, $taxonomy, true );
// Get existing attributes
$product_attributes = get_post_meta( $product_id, '_product_attributes', true);
// get the count of existing attributes to set the "position" in the array
$count = count($product_attributes);
// Insert new attribute in existing array of attributes (if there is any)
$product_attributes[$taxonomy] = array(
'name' => $taxonomy,
'value' => '',
'position' => $count, // added
'is_visible' => '0',
'is_variation' => '0', // added (set the right value)
'is_taxonomy' => '1'
);
// Save the data
update_post_meta( $product_id, '_product_attributes', $product_attributes );
Note : I replaced (manually) $taxonomy -> 'pa_list' and $clean_keywords = array('X345W','X465W');
In attribute field 'pa_list' both partner codes have been added but do not fill or set to product.
I'm having a hard time connecting some logic here.
I hit a dead end to get what I wanted.
Can anyone help me?
Thank You

Filter products by price range using WooCommerce wc_get_products

I'm getting a bunch of paginated variable products using wc_get_products(). And I would like to only get products in a given price-range. The problem is that there are a lot of products, so only 20 products are fetched at a time.
According to the documentation, there are no meta_query()-option when using wc_get_products().
So how do I get this?
Here is my current query:
$products_query = [
'category' => [ get_queried_object()->slug ],
'limit' => 20,
'paginate' => true,
'paged' => (get_query_var('paged')) ? get_query_var('paged') : 1,
];
$products = wc_get_products( $products_query );
Now remember, that it's something that needs to be implemented before the products are queried from the database.
Let's say that I have 100 products total. Since the products are paginated, then if I have ordered the products after _price (ASC), then page 1 should return the 20 cheapest products. And page 3 should return the products 40-60 (after they have been sorted).
Solution attempts
Attempt 1 - Try using a meta_query anyways
I figured that wc_get_products was inspired and built on top of WP_Query, so I tried this:
$products_query = [
'category' => [ get_queried_object()->slug ],
'limit' => 20,
'paginate' => true,
'paged' => (get_query_var('paged')) ? get_query_var('paged') : 1,
'meta_query' => [
'relation' => 'AND',
[
'key' => '_price',
'value' => 100,
'compare' => '<=',
'type' => 'NUMERIC'
]
]
];
$products = wc_get_products( $products_query );
It just ignores the meta_query-part and returns a result as if it wasn't there.
Attempt 2 - Using WP_Query
Everywhere I look, there are arrows pointing towards wc_get_products and away from wp_query.
So I haven't pursued this.
Attempt 3 - Raw SQL
One solution would be to make a raw SQL-query. But since the products are variable products, then the SQL-query would be quite a few joins, since it should first find all products - and then find all variations. And then sort the products after the lowest variation-price (if the price-sort is ascending) - and after the highest variation-price (if the price-sort is descending). It's obviously possible, but I was hoping to find a more WordPress-like solution. And I'm not good at SQL.
Attempt 4 - Using a plugin
I look around a bit - and sort'n'filter-plugins for WooCommerce are monsters, since they slab scripts and styles left, right and center. So this seems like a poor solution.
Attempt 5 - Using WooCommerce API
I can see that min_price and max_price are mentioned under List all products. So this might actually work.
Variable products are something complicated to filter by price… So the following is not perfect, but show you a way to enable a price range in WC_Product_Query.
So the following function will enable a custom price range query on WC_Product_Query:
add_filter( 'woocommerce_product_data_store_cpt_get_products_query', 'handle_price_range_query_var', 10, 2 );
function handle_price_range_query_var( $query, $query_vars ) {
if ( ! empty( $query_vars['price_range'] ) ) {
$price_range = explode( '|', esc_attr($query_vars['price_range']) );
if ( is_array($price_range) && count($price_range) == 2 ) {
$query['meta_query']['relation'] = 'AND';
$query['meta_query'][] = array(
'key' => '_price',
'value' => reset($price_range), // From price value
'compare' => '>=',
'type' => 'NUMERIC'
);
$query['meta_query'][] = array(
'key' => '_price',
'value' => end($price_range), // To price value
'compare' => '<=',
'type' => 'NUMERIC'
);
$query['orderby'] = 'meta_value_num'; // sort by price
$query['order'] = 'ASC'; // In ascending order
}
}
return $query;
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
EXAMPLE USAGE
Handle float numbers.
The 1st price is separated from the 2nd one by a pipe |
Here we query products from $10.25 to $50 (price range):
$products = wc_get_products( [
'limit' => 20,
'status' => 'publish',
'price_range' => '10.25|50', // The first price is separated from the 2nd one with a pipe
] );
echo '<ul>';
foreach( $products as $product ) {
echo '<li>'.$product->get_name().' '.$product->get_price_html().'</li>';
}
echo '</ul>';
To filter only variable products, you can add the following line to the query:
'type' => 'variable',
Documentation: wc_get_products and WC_Product_Query
function handle_custom_query($query, $query_vars)
{
if (!empty($query_vars['meta_query'])) {
foreach ($query_vars['meta_query'] as $q) {
$query['meta_query'][] = $q;
}
}
return $query;
}
add_filter('woocommerce_product_data_store_cpt_get_products_query', 'handle_custom_query', 10, 3);
usage:
$query = new \WC_Product_Query(
array(
// ...standard queries
// https://github.com/woocommerce/woocommerce/wiki/wc_get_products-and-WC_Product_Query
'meta_query' => array(
// ...array of meta queries
),
)
);
$products = $query->get_products();

How update product attributes programmatically?

I use following code to add attributes to a product:
foreach ($_attributes as $name => $value) {
wp_set_object_terms($post_id, $value, $name, true);
$product_attributes[$name] = array (
'name' => $name, // set attribute name
'value' => $value, // set attribute value
'is_visible' => 0,
'is_variation' => 1,
'is_taxonomy' => 1
);
}
update_post_meta($post_id, '_product_attributes', $product_attributes );
but it remove previous attributes I added in product edit in admin, like product brand or model.
How can I update current product attributes without remove previous ones?
Thanks to help me.
You could simply backup your DB content before updating, like this:
$tmpBk = get_post_meta($post_id, '_product_attributes',true);
update_post_meta($post_id, '_product_attributes', array_merge(tmpBk,$product_attributes) );
that should be enough to save your previously stored values

woocommerce product attribute not working programmatically

i want to apply attribute programmatically in woocommerce, for that i have used this code but it is not working, can anyone please look my code and help me to resolve this issue ?
$product_id = 4931;
$attribute_name = "test_attribute";
$attribute_value = "test_attribute";
$term_taxonomy_ids = wp_set_object_terms($product_id, $attribute_value, $attribute_name, true);
$data = array(
$attribute_name => array(
'name' => $attribute_name,
'value' => '',
'is_visible' => '1',
'is_variation' => '0',
'is_taxonomy' => '1'
)
);
//First getting the Post Meta
$_product_attributes = get_post_meta($product_id, '_product_attributes', TRUE);
//Updating the Post Meta
update_post_meta($product_id, '_product_attributes', array_merge($_product_attributes, $data));
}
I recommend to use the wc-functions to handle product data, because in future product data maybe not in wp_post and wp_post_meta table anymore.
$product_id = 4931;
$p = wc_get_product( $product_id );
$my_custom_value = $p->get_meta('_custom_value');
// to change the costum attribute
$p->update_meta_data('_custom_value', $my_new_value );
$p->save();
This links has usefull information:
wc_get_products and WC_Product_Query
Accessing WC_Product protected data in Woocommerce 3

How to automatically set cross-sells in WooCommerce based on product category

I'm trying to edit the cross-sell section of the cart page in WooCommerce. I want to display random products from the same category in the cross-sell section.
For example, someone adds something from the category women's fashion then it will display other products in the cross-sell section from the same category.
or if they have items from multiple categories to then just pick two at random.
Is there a way of doing this or is it a case of having to go through every product individually to select the cross-sell products?
Following code returns automatically $cross_sells IDs that belong to the product categor(y)(ies) from the products in the cart.
function filter_woocommerce_cart_crosssell_ids( $cross_sells, $cart ) {
// Initialize
$product_cats_ids = array();
$product_cats_ids_unique = array();
foreach ( $cart->get_cart() as $cart_item ) {
// Get product id
$product_id = $cart_item['product_id'];
// Get current product categories id(s) & add to array
$product_cats_ids = array_merge( $product_cats_ids, wc_get_product_term_ids( $product_id, 'product_cat' ) );
}
// Not empty
if ( !empty( $product_cats_ids ) ) {
// Removes duplicate values
$product_cats_ids_unique = array_unique( $product_cats_ids, SORT_REGULAR );
// Get product id(s) from a certain category, by category-id
$product_ids_from_cats_ids = get_posts( array(
'post_type' => 'product',
'numberposts' => -1,
'post_status' => 'publish',
'fields' => 'ids',
'tax_query' => array(
array(
'taxonomy' => 'product_cat',
'field' => 'id',
'terms' => $product_cats_ids_unique,
'operator' => 'IN',
)
),
) );
// Removes duplicate values
$cross_sells = array_unique( $product_ids_from_cats_ids, SORT_REGULAR );
}
return $cross_sells;
}
add_filter( 'woocommerce_cart_crosssell_ids', 'filter_woocommerce_cart_crosssell_ids', 10, 2 );
Note: 'numberposts' => -1 means ALL, this can be adapted to your needs
The limit ('posts_per_page') can be set via the woocommerce_cross_sells_total filter hook
function filter_woocommerce_cross_sells_total( $limit ) {
// Set limit
$limit = 4;
return $limit;
}
add_filter( 'woocommerce_cross_sells_total', 'filter_woocommerce_cross_sells_total', 10, 1 );

Resources