Woocommerce product link url for simple products - wordpress

Using the shortcode [add_to_cart_url sku="#"] will generate a link that will take the shopper to the single product page ONLY IF the product has variations.
What if I want a simple product (a product without variations) to link to its single product page instead of adding it to the cart?
IMO, this seems overlooked by the WooCommerce team.
All I want is a generated link to an item's single product page based on its SKU. Something as simple as implementing [product_url sku="#"] would be great.
Otherwise, searching Google has revealed no success.

Is this what you want ?
Below code is for the shortcode of Product URL only and you can use it with like :
[product_url sku="SKUofProduct"] or with ID [product_url id="productID"]
add_shortcode( 'product_url', 'rohils_product_url_function' );
function rohils_product_url_function($atts){
global $wpdb;
if ( empty( $atts ) ) {
return '';
}
if ( isset( $atts['id'] ) ) {
$product_data = get_post( $atts['id'] );
} elseif ( isset( $atts['sku'] ) ) {
$product_id = wc_get_product_id_by_sku( $atts['sku'] );
$product_data = get_post( $product_id );
} else {
return '';
}
if ( 'product' !== $product_data->post_type ) {
return '';
}
$_product = wc_get_product( $product_data );
return esc_url( get_post_permalink($_product->id) );
}

Related

Add suffix text to specific product category in Woocommerce [duplicate]

I need to add 'per metre' to the price on most of my online catalogue, I tried the code on this thread in my finctions.php but I cannot get it to omit/include particular categories- it seems to be all or nothing. What am I doing wrong?
I have edited the code as such:
/*add 'per metre' after selected items*/
add_filter( 'woocommerce_get_price_html', 'conditional_price_suffix', 20, 2 );
function conditional_price_suffix( $price, $product ) {
// HERE define your product categories (can be IDs, slugs or names)
$product_categories = array('fabric','haberdashery', 'lining',);
if( ! has_term( $product_categories, 'fasteners', 'patches', 'remnnants', $product->get_id() ) )
$price .= ' ' . __('per metre');
return $price;
}
I want 'fabrics', 'haberdashery', 'lining' to show per metre, and 'fasteners', 'patches', 'remnants' to NOT show the suffix.
I have tried variations of the code -my exclusions in the top bit and the inclusions in the second part, and with/without the "( ! has term" section, but whichever I do takes all the suffix messages away, or applies to all categories.
It would be amazing if I could get this to work as have previously been using a very bloated plug-in. I'm only basically capable in this stuff so please feel free to talk me through it as if I am an idiot.
There is a little mistake in your code in the has_term() function.
To handle parent product categories, we will use a custom conditional function instead of has_tem().
I have also added some code to handle the product variation selected price of variable products, So try this instead:
// Custom conditional function that handle parent product categories too
function has_product_categories( $categories, $product_id = 0 ) {
$parent_term_ids = $categories_ids = array(); // Initializing
$taxonomy = 'product_cat';
$product_id = $product_id == 0 ? get_the_id() : $product_id;
if( is_string( $categories ) ) {
$categories = (array) $categories; // Convert string to array
}
// Convert categories term names and slugs to categories term ids
foreach ( $categories as $category ){
$result = (array) term_exists( $category, $taxonomy );
if ( ! empty( $result ) ) {
$categories_ids[] = reset($result);
}
}
// Loop through the current product category terms to get only parent main category term
foreach( get_the_terms( $product_id, $taxonomy ) as $term ){
if( $term->parent > 0 ){
$parent_term_ids[] = $term->parent; // Set the parent product category
$parent_term_ids[] = $term->term_id; // (and the child)
} else {
$parent_term_ids[] = $term->term_id; // It is the Main category term and we set it.
}
}
return array_intersect( $categories_ids, array_unique($parent_term_ids) ) ? true : false;
}
add_filter( 'woocommerce_get_price_html', 'conditional_price_suffix', 10, 2 );
function conditional_price_suffix( $price, $product ) {
// Handling product variations
$product_id = $product->is_type('variation') ? $product->get_parent_id() : $product->get_id();
// HERE define your product categories (can be IDs, slugs or names)
$product_categories = array('fabric','haberdashery', 'lining');
if( has_product_categories( $product_categories, $product_id ) )
$price .= ' ' . __('per metre');
return $price;
}
Code goes in function.php file of your active child theme (or active theme). tested and works.

How to prevent WooCommerce composite product from adding same product of different component in Cart

I have configured WooCommerce composite product's component in the following manner ( see below 2 screenshots ).
On the front-end side of product single page , I am selecting the same product ( eg : Form D ) from those 2 components ( see below screenshot ).
Now , when I click on Add to cart button , it is showing a Notice like : only 1 product may allow adding to cart , but also it is adding the same product to the cart ( see below screenshot ).
Now , to prevent adding same products in the cart , I write following code :
add_filter( 'woocommerce_add_to_cart_validation', 'filter__woocommerce_add_to_cart_validation', 10, 3 );
function filter__woocommerce_add_to_cart_validation ( $passed, $product_id, $quantity ){
$all_pro_id = array();
if ( isset( $_POST['wccp_component_selection'] ) && ! empty( $_POST['wccp_component_selection'] ) ) {
foreach ( $_POST['wccp_component_selection'] as $key => $value ) {
if ( ! empty( $value ) ) {
$all_pro_id[] = $value;
}
}
//echo "<pre>"; print_r($all_pro_id); die;
if ( count( $all_pro_id ) > count( array_unique( $all_pro_id ) ) ) {
//echo "yes";
$passed = false;
}
}
return $passed;
}
Still , the code is not working and it is adding the same product to the cart ( see below screenshot ).

Saving custom WooCommerce cart data to order product item meta

I'm attempting to add meta data from the cart meta into the products in an order. So far I've been successful in adding a custom value to the products at the cart level (proved by outputting the values inline on the cart table), but once the final checkout has been done its not saving anywhere.
Adding to the cart (confirmed working):
function se_wc_add_product_order_type_cart( $cart_item, $product_id ) {
$product_order_type = $_POST['product_order_type'] ?? '';
if ( $product_order_type ) {
$cart_item['product_order_type'] = sanitize_text_field( $product_order_type );
}
return $cart_item;
}
add_filter( 'woocommerce_add_cart_item_data', 'se_wc_add_product_order_type_cart', 10, 2 );
Adding to the order from the cart (not working):
function se_wc_product_add_on_order_item_meta( $item, $cart_item_key, $values, $order ) {
$product_order_type = $values['product_order_type'] ?? '';
if ( ! empty( $product_order_type ) ) {
$item->add_meta_data( 'product_order_type', $product_order_type );
}
}
add_action( 'woocommerce_checkout_create_order_line_item', ' se_wc_product_add_on_order_item_meta', 10, 4 );
There's nothing being saved to the database (assuming its supposed to go in the wp_woocommerce_order_itemmeta table), and doing a var_dump on the meta_data field from the woocommerce_order_item_get_formatted_meta_data hook is empty - presumably again where it would show up.
If it matters, the product is a variable product and the site is a multisite.
You can use below code to add item meta to order meta:
function se_wc_product_add_on_order_item_meta( $item_id, $values ) {
$product_order_type = $values['product_order_type'];
if ( ! empty( $product_order_type ) ) {
wc_add_order_item_meta($item_id,'product_order_type',$product_order_type);
}
}
add_action( 'woocommerce_new_order_item', ' se_wc_product_add_on_order_item_meta', 1, 2 );
Thanks.

WooCommerce redirect add to cart to checkout for specific product types

In Woocommerce i need to skip the cart page when adding products and redirect directly to checkout only for subscription product types.
I found the below code somewhere else which works for skipping the cart page based on product ID, however I couldn't get it right to use product type instead (see further below for what I tried).
function woocommerce_skip_cart() {
global $woocommerce;
$product_id = (int) apply_filters( 'woocommerce_add_to_cart_product_id', $_POST['add-to-cart'] );
if ( $product_id == 31 || $product_id == 31 ) {
$checkout_url = WC()->cart->get_checkout_url();
return $checkout_url;
}
}
add_filter ('woocommerce_add_to_cart_redirect', 'woocommerce_skip_cart');
What I tried: (it breaks the whole site: becomes a blanc page)
function woocommerce_skip_cart() {
global $woocommerce;
$product_id = (int) apply_filters( 'woocommerce_add_to_cart_product_id', $_POST['add-to-cart'] );
$product = wc_get_product( $product_id );
if ( $product->is_type( 'subscription' ) ){
$checkout_url = WC()->cart->get_checkout_url();
return $checkout_url;
}
}
add_filter ('woocommerce_add_to_cart_redirect', 'woocommerce_skip_cart');
Any idea's how to achieve this?
I revised your code. try the below code.
function woocommerce_skip_cart( $url ) {
$product_id = (int) apply_filters( 'woocommerce_add_to_cart_product_id', $_POST['add-to-cart'] );
if( $product_id ){
$product = wc_get_product( $product_id );
if ( $product->is_type( 'subscription' ) || $product->is_type( 'variable-subscription' ) ){
$checkout_url = WC()->cart->get_checkout_url();
return $checkout_url;
}
}
return $url;
}
add_filter ('woocommerce_add_to_cart_redirect', 'woocommerce_skip_cart');

Changing product name on checkout and order on WooCommerce with WPML

I am using WooCommerce with WPML plugin. I want to implement a feature on checkout when a customer under certain conditions can have an upgrade of his product but keeping the old product price.
The products are variable with many variable attributes.
So, more specifically, what I want is if a customer has selected a specific product variation with x price on checkout (under a certain condition) I could change his cart item with another product's variation but keep the x price.
What I tried first is to change only the name of the product using woocommerce_order_item_name hook but the change doesn't follow on the order. This is important because some order data are then sent to an API.
Afterwards I used "Changing WooCommerce cart item names" answer code, which worked perfectly for my purpose until I installed WPML. For some reason the WC_Cart method set_name() doesn't work with WPML. I opened a support thread but they still can't find a solution.
Can anyone suggest any other solution?
Update
I have tried an approach where I remove the product item on cart and then I add the one I need. After I use set_price() to change the price of the newly added item. The removal/addition seems to be working but the price is not changed on one language and it is not applied on both languages after placing order.
This is the code I use:
function berrytaxiplon_change_product_name( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// Loop through cart items
foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) {
// Get an instance of the WC_Product object
$product = $cart_item['data'];
// Get the product name (Added Woocommerce 3+ compatibility)
$product_id = method_exists( $product, 'get_parent_id' ) ? $product->get_parent_id() : $product->post->post_parent;
if ( ICL_LANGUAGE_CODE == 'en') {
if (isset($cart_item['s-member-level']) && $cart_item['s-member-level'] == 3 && $product_id == 12) {
$new_product = wc_get_product( 82 );
$atrributes = $product->get_attributes('view');
foreach ($atrributes as $atrribute_key => $atrribute_value) {
$new_attributes['attribute_' . $atrribute_key] = strtolower($atrribute_value);
}
$new_variation_id = find_matching_product_variation_id(82, $new_attributes);
$cart->remove_cart_item( $cart_item_key );
$cart->add_to_cart( 82, 1, $new_variation_id, $new_attributes, $cart_item );
foreach ( WC()->cart->get_cart() as $new_item ) {
$new_item['data']->set_price( $cart_item['s-fare'] );
}
}
} else {
if (isset($cart_item['s-member-level']) && $cart_item['s-member-level'] == 3 && $product_id == 282) {
$new_product = wc_get_product( 303 );
$atrributes = $product->get_attributes('view');
foreach ($atrributes as $atrribute_key => $atrribute_value) {
$new_attributes['attribute_' . $atrribute_key] = strtolower($atrribute_value);
}
$new_variation_id = find_matching_product_variation_id(303, $new_attributes);
$cart->remove_cart_item( $cart_item_key );
$cart->add_to_cart( 303, 1, $new_variation_id, $new_attributes, $cart_item );
foreach ( WC()->cart->get_cart() as $new_item ) {
$new_item['data']->set_price( $cart_item['s-fare']);
}
}
}
}
}
add_action( 'woocommerce_before_calculate_totals', 'berrytaxiplon_change_product_name', 10, 1 );
Any idea why the set_price() method is not applied?
Update 2
WPMl uses 'woocommerce_before_calculate_totals' and overrides the action added on functions.php
WPML support provided a solution using 3 filters:
https://wpml.org/forums/topic/cant-use-set_name-method-for-the-product-object-on-checkout/#post-3977153
So this is a code that I am using in one of my projects to add a product variation to cart based off of some filters and the selected product:
$product = new WC_Product($product_id); //The main product whose variation has to be added
$product_name = $product->get_name(); //Name of the main product
$quantity = sanitize_text_field($cData['quantity']); //You can set this to 1
$variation_id = sanitize_text_field($cData['variation_id']); //I had the variation ID from filters
$variation = array(
'pa_duration' => sanitize_text_field($cData['duration']) //The variation slug was also available for me.
);
$cart_item_data = array('custom_price' => sanitize_text_field($custom_price));
$cart = WC()->cart->add_to_cart( (int)$product_id, (int)$quantity, (int)$variation_id, $variation, $cart_item_data ); //This will add products to cart but with the actual price of the variation being added and meta data holding the custom price.
WC()->cart->calculate_totals();
WC()->cart->set_session();
WC()->cart->maybe_set_cart_cookies();
Then you need to do a check on before cart totals are calculated and set the price to custom price like this:
function woocommerce_custom_price_to_cart_item( $cart_object ) {
if( !WC()->session->__isset( "reload_checkout" )) {
foreach ( $cart_object->cart_contents as $key => $value ) {
if( isset( $value["custom_price"] ) ) {
$value['data']->set_price($value["custom_price"]);
}
}
}
}
add_action( 'woocommerce_before_calculate_totals', 'woocommerce_custom_price_to_cart_item', 99 );
The code provided from Faham is very helpful but the page-template that leads to checkout is already over-complicated so I focused to use his logic on the 'woocommerce_before_calculate_totals' hook I am trying all along.
So instead of trying to change the name I remove the item and add the new one. Then calling a new loop I set the price to be of the item that was removed.
function berrytaxiplon_change_product_name( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// Loop through cart items
foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) {
// Get an instance of the WC_Product object
$product = $cart_item['data'];
// Get the product name (Added Woocommerce 3+ compatibility)
$product_id = method_exists( $product, 'get_parent_id' ) ? $product->get_parent_id() : $product->post->post_parent;
if ( ICL_LANGUAGE_CODE == 'en') {
if (isset($cart_item['s-member-level']) && $cart_item['s-member-level'] == 3 && $product_id == 12) {
// SET THE NEW NAME
$new_product = wc_get_product( 82 );
$atrributes = $product->get_attributes('view');
foreach ($atrributes as $atrribute_key => $atrribute_value) {
$new_attributes['attribute_' . $atrribute_key] = strtolower($atrribute_value);
}
$new_variation_id = find_matching_product_variation_id(82, $new_attributes);
$cart->remove_cart_item( $cart_item_key );
$cart->add_to_cart( 82, 1, $new_variation_id, $new_attributes, $cart_item );
foreach ( WC()->cart->get_cart() as $new_item ) {
$new_item['data']->set_price( get_post_meta( $cart_item['variation_id'], '_price', true ) );
}
}
} else {
if (isset($cart_item['s-member-level']) && $cart_item['s-member-level'] == 3 && $product_id == 282) {
// SET THE NEW NAME
$new_product = wc_get_product( 303 );
$atrributes = $product->get_attributes('view');
foreach ($atrributes as $atrribute_key => $atrribute_value) {
$new_attributes['attribute_' . $atrribute_key] = strtolower($atrribute_value);
}
$new_variation_id = find_matching_product_variation_id(303, $new_attributes);
$cart->remove_cart_item( $cart_item_key );
$cart->add_to_cart( 303, 1, $new_variation_id, $new_attributes, $cart_item );
foreach ( WC()->cart->get_cart() as $new_item ) {
$new_item['data']->set_price( get_post_meta( $cart_item['variation_id'], '_price', true ) );
}
}
}
}
}
add_action( 'woocommerce_before_calculate_totals', 'berrytaxiplon_change_product_name', 10, 1 );
I use the function below to match the attributes taken from the question WooCommerce: Get Product Variation ID from Matching Attributes
function find_matching_product_variation_id($product_id, $attributes)
{
return (new \WC_Product_Data_Store_CPT())->find_matching_product_variation(
new \WC_Product($product_id),
$attributes
);
}
I am a little skeptical using add_to_cart() and a second foreach() inside the $cart_item. But I tested and it seems to work without errors.
Update
Actually there is an issue with this code (or with WPML again). It seems that set_price() is not applied on the secondary language. Yet if I reload the checkout page an send the data again the new price is applied.

Resources