I have constructed the below function to work in conjunction with a button displayed on the edit product page. It is designed to generate some description text based on the title and SKU of the product. The code works perfectly for 'simple' products, but I am struggling to get it to work for 'variable' products too.
What exactly do I need to do to get it to work correctly for both simple and variable products?
The current behaviour is:
When triggered on a simple product, it adds the new description or updates the old one to the new one.
When triggered on a variable product, it updates the main description, but deletes all variations which were previously set up on the product.
add_action('woocommerce_process_product_meta', 'update_and_save_utapd');
function update_and_save_utapd() {
if(isset($_POST['button_new_update_description'])){
$product_id = wc_get_product(get_the_ID());
$wcProduct = new WC_Product($product_id);
$get_title = $wcProduct->get_name();
$get_mpn = $wcProduct->get_meta('sp_wc_barcode_field');
$get_description = $wcProduct->get_description();
$output = '';
if(!empty($get_title)){
$output .= "<p>The " . $get_title;
}if(!empty($get_mpn)){
$output .= " (MPN: " . $get_mpn . ").";
}if(!empty($get_description)){
$wcProduct->set_description($output);
$wcProduct->save();
return "<div>SUCCESS: YOU HAVE UPDATED YOUR DESCRIPTION.</div>";
}elseif(empty($get_description)){
$wcProduct->set_description($output);
$wcProduct->save();
return "<div>SUCCESS: YOU HAVE GENERATED A NEW DESCRIPTION.</div>";
}
}
}
First when using action hooks in backend that save product data, you can't return a string (a text) as you are trying to do and anyway, it will never be displayed
Now since WooCommerce 3 you can use woocommerce_admin_process_product_object much better hook that include the WC_Product Object as function argument and there is no need to use save() method at the end of your code as once this hook is triggered the save() method is auto applied.
So we can simplify your code:
add_action('woocommerce_admin_process_product_object', 'update_and_save_utapd');
function update_and_save_utapd( $product ) {
if( isset($_POST['button_new_update_description']) ){
$name = $product->get_name();
$barcode = $product->get_meta('sp_wc_barcode_field');
$output = '';
if ( ! empty($name) ) {
$output .= "<p>The " . $name;
}
if ( ! empty($barcode) ) {
$output .= " (MPN: " . $barcode . ").";
}
$product->set_description( $output );
}
}
Code goes in functions.php file of the active child theme (or active theme). It should better work now, without throwing errors.
With the woocommerce_process_product_meta hook you already have the product id and the product object available. Here you will find more information.
To verify that the button has been clicked you must also check its value, in addition to the isset() function.
Replace value_button with the value of the element's value
attribute
add_action('woocommerce_process_product_meta', 'update_and_save_utapd', 10, 2 );
function update_and_save_utapd( $product_id, $product ) {
// replace "value_button" with the value of the element's "value" attribute
if ( isset( $_POST['button_new_update_description'] ) && $_POST['button_new_update_description'] == 'value_button' ) {
if ( $product->is_type('simple') || $product->is_type('variable') ) {
$title = $product->get_name();
$mpn = $product->get_meta('sp_wc_barcode_field');
$description = $product->get_description();
$output = '';
if ( ! empty($title) ) {
$output .= "<p>The " . $title;
}
if ( ! empty($mpn) ) {
$output .= " (MPN: " . $mpn . ").";
}
if ( ! empty($get_description) ) {
$product->set_description($output);
$product->save();
return "<div>SUCCESS: YOU HAVE UPDATED YOUR DESCRIPTION.</div>";
} else {
$product->set_description($output);
$product->save();
return "<div>SUCCESS: YOU HAVE GENERATED A NEW DESCRIPTION.</div>";
}
}
}
}
Related
I would like to use an ACF field to inject Schema merkup to a few specific pages on my WordPress website. Some of them are custom taxonomies or custom post types.
After a two hour research on the topic, I am still stuck.
I have created a text area field called schema_code and entered the desired Schema markup for some of my sub pages.
I currently use this code in my functions.php which does not do anything:
function acf_header_script() {
$schema = get_field('schema_code');
echo '<script type=application/ld+json>' . json_encode($schema_code) . '</script>';
}
add_action ( 'wp_head', 'acf_header_script' );
What am I missing here? Thanks a lot!
The second parameter of the ACF get_field() is required in this case, since you're not in the loop. It is either the post->ID or it's the taxonomy ID where it's {taxonomy_name}_{taxonomy_id} https://www.advancedcustomfields.com/resources/get_field/
Since you want to do this on pages and archives, etc... You need to first determine if it's a single page or an archive, etc.
function acf_header_script() {
// is it a single post?
if ( ! is_single() ) {
// no? get the queried object.
$object = get_queried_object();
if ( is_a( $object, 'WP_POST' ) ) {
$param = $object->ID;
} else {
$param = $object->taxonomy . '_' . $object->term_id;
}
} else {
// yes it's a single.
global $post;
$param = $post->ID;
}
$schema = get_field( 'schema_code', $param );
// if $schema is not empty.
if ( $schema ) {
echo '<script type=application/ld+json>' . json_encode( $schema ) . '</script>';
}
}
add_action( 'wp_head', 'acf_header_script' );
I am using the following code to show custom product meta in the order items table on the WooCommerce admin order details page:
add_action('woocommerce_admin_order_item_headers', 'tempiconsegna_admin_order_item_headers');
function tempiconsegna_admin_order_item_headers() {
$column_name = 'Tempi Consegna';
echo '<th>' . $column_name . '</th>';
}
add_action('woocommerce_admin_order_item_values', 'tempiconsegna_admin_order_item_values', 10, 3);
function tempiconsegna_admin_order_item_values($_product, $item, $item_id = null) {
$value = get_post_meta($_product->post->ID, 'prefix-tempiconsegna', 1);
echo '<td>' . $value . '</td>';
}
It displays "prefix-tempiconsegna" which are custom metas like:
Available in 3 days
Available now
etc..
My problem is that if I change the availability in the product, it changes also in previous orders.
How do i make this displaying the value at the moment of the order without changing when I update the availability of the product?
Your current code contains 2 errors:
Attempt to read property "post" on null
Attempt to read property "ID" on null
To answer your question: that's because you're using get_post_meta() and the productID, so if you adjust the data for the product it will also change the data where it is displayed, in your case the current and previous orders.
To prevent this, you have to add the data per order line item, this can be done via:
function action_woocommerce_checkout_create_order_line_item( $item, $cart_item_key, $values, $order ) {
// The WC_Product instance Object
$product = $item->get_product();
// Get value
$value = $product->get_meta( 'prefix-tempiconsegna' );
// NOT empty
if ( ! empty ( $value ) ) {
$item->update_meta_data( 'prefix-tempiconsegna', $value );
}
}
add_action( 'woocommerce_checkout_create_order_line_item', 'action_woocommerce_checkout_create_order_line_item', 10, 4 );
Then to display this in WooCommerce admin order details page, use:
// Add header
function action_woocommerce_admin_order_item_headers( $order ) {
// Set the column name
$column_name = __( 'Tempi Consegna', 'woocommerce' );
// Display the column name
echo '<th class="my-class">' . $column_name . '</th>';
}
add_action( 'woocommerce_admin_order_item_headers', 'action_woocommerce_admin_order_item_headers', 10, 1 );
//Add content
function action_woocommerce_admin_order_item_values( $product, $item, $item_id ) {
// Only for "line_item" items type, to avoid errors
if ( ! $item->is_type('line_item') ) return;
// Get value
$value = $item->get_meta( 'prefix-tempiconsegna' );
// NOT empty
if ( ! empty ( $value ) ) {
echo '<td>' . $value . '</td>';
} else {
echo '<td>Meta not found!</td>';
}
}
add_action( 'woocommerce_admin_order_item_values', 'action_woocommerce_admin_order_item_values', 10, 3 );
I'm working in a woocommerce site (Real Estate) and I don't get to show product custom attributes on my front shop page (Eg. Square meters, Rooms, Toilets, etc). I'm trying to use the code below but I can make it work. With the code I have I'm just able to show product ID but when it comes to attributes it only shows the word "Array" or just "Nothing" when I put the attributes IDs in the code.
The is the code I have:
add_action('woocommerce_after_shop_loop_item_title', 'cstm_display_product_category', 5);
function cstm_display_product_category()
{
global $product;
$productAttribute = $product->get_id();
//if(isset($productAttribute)){
echo '<div class="items" style="color: #fff;"><p>Output: ' . $productAttribute . '</p></div>';
//}
}
You can see the output live here
Whatever help or guide you can give me to be able to achieve this, will be so much appreciated. I'm a noob in this matters.
P.D. I'm inserting this code in my functions.php file.
Wordpress is up to date.
Woocommerce is up to date.
Update 3
With product attributes (custom or not), you can use the WC_Product method get_attribute() where you can input an attribute name, slug or taxonomy. So in your code:
add_action('woocommerce_after_shop_loop_item_title', 'display_custom_product_attributes_on_loop', 5 );
function display_custom_product_attributes_on_loop() {
global $product;
$value1 = $product->get_attribute('Square meters');
$value2 = $product->get_attribute('Rooms');
$value3 = $product->get_attribute('Toilets');
if ( ! empty($value1) || ! empty($value2) || ! empty($value3) ) {
echo '<div class="items" style="color: red;"><p>';
$attributes = array(); // Initializing
if ( ! empty($value1) ) {
$attributes[] = __("Square meters:") . ' ' . $value1;
}
if ( ! empty($value2) ) {
$attributes[] = __("Rooms:") . ' ' . $value2;
}
if ( ! empty($value3) ) {
$attributes[] = __("Toilets:") . ' ' . $value3;
}
echo implode( '<br>', $attributes ) . '</p></div>';
}
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
Automate the code from a simple array of product attribute names:
The code below will gives the same output, but it's compacted, optimized and requires just an array of your desired product attributes:
add_action('woocommerce_after_shop_loop_item_title', 'display_custom_product_attributes_on_loop', 5 );
function display_custom_product_attributes_on_loop() {
global $product;
// Settings: Here below set your product attribute label names
$attributes_names = array('Square meters', 'Rooms', 'Toilets');
$attributes_data = array(); // Initializing
// Loop through product attribute settings array
foreach ( $attributes_names as $attribute_name ) {
if ( $value = $product->get_attribute($attribute_name) ) {
$attributes_data[] = $attribute_name . ': ' . $value;
}
}
if ( ! empty($attributes_data) ) {
echo '<div class="items" style="color: red;"><p>' . implode( '<br>', $attributes_data ) . '</p></div>';
}
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
I am trying to have it so for specific product attribute that contain "agave" text is displayed in the short description.
I have tried a few snippets of code but none seem to work. I have no problem getting them to work with Categories but I just want it for certain attributes of the products - Agave
function filter_woocommerce_short_description( $post_excerpt ) {
global $post;
if ( has_term( "agave", "categories", $post->ID ) ) {
$post_excerpt .= "<br/>" . "Text Here";
}
return $post_excerpt;
};
add_filter('woocommerce_short_description', 'filter_woocommerce_short_description',10, 1 );
I expect the text to show up under the certain attributes (Agave) but they do not
I have tried to use this
add_filter('woocommerce_short_description',
'filter_woocommerce_short_description',10, 1 );
function filter_woocommerce_short_description( $short_description ) {
global $product;
$string_values = $product->get_attribute('agave');
if ( strpos($string_values, 'agave') !== false ) {
$short_description .= '<br>' . __("Testing This Out - AGAVE");
}
return $short_description;
}
For a specific product attribute "agave" you will use something a bit different:
add_filter('woocommerce_short_description', 'filter_woocommerce_short_description',10, 1 );
function filter_woocommerce_short_description( $short_description ) {
global $product;
$string_values = $product->get_attribute('agave');
if ( ! empty($string_values) ) {
$short_description .= '<br>' . __("Text Here");
}
return $short_description;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
Now if "agave" is a term of a product attribute, you should need to set the product attribute name in $product->get_attribute('attribute-name');
and replace the condition by something like:
if ( strpos($string_values, 'agave') !== false ) {
Note: The taxonomy for product category is product_cat, but not categories…
add_filter('woocommerce_short_description', 'filter_woocommerce_short_description',10, 1 );
function filter_woocommerce_short_description( $short_description ) {
global $product;
$string_values = $product->get_attribute('agave');
$agave = $attributes["agave"];
if ( $agave ) {
$short_description .= '<br>' . __("Testing This Out - AGAVE");
}
return $short_description;
}
I have created a custom column with a custom plugin.
I dont know if I can use the following to populate this column with a value from an xml.
//POPULATE COLUMN
add_action('manage_posts_custom_column', 'wnetpp_populate_custom_columns3', 10, 2);
function wnetpp_populate_custom_columns3( $column, $post_id ) {
if ($column == 'test_column3') {
$current_product = wc_get_product( $post_id );
$product_sku = $current_product->get_sku();
read_parse_xml($product_sku)
} }
and with a function like the following:
function read_parse_xml($product_sku)
{
$url = 'https://example.com/ProductsUpdates4.xml';
$xml = file_get_contents($url);
$xml = simplexml_load_string($xml);
foreach($xml as $x) {
$sku = $x->sku;
$bfsku = $x->bf_sku;
$suppliersku = $x->supplier_sku;
$price = $x->price;
$availability = $x->availability;
if($sku==$product_sku)
{
echo '<div id="_supprice-' . $post_id . '">' . $price . '</div>';
}
}
}
But nothing is working.
I get a blank page for products.
How I am supposed to check if xml has been accessed.
First I forgot to put a semicolumn ; after read_parse_xml($product_sku)
For each column I created a different read_parse_xml($product_sku) function called read_parse_xml1 2 3 etc.
and I used number_format to format my string to Number like
if($sku==$product_sku)
{
echo '<div id="_supprice-' . $post_id . '">' . number_format( (float)$price,2) . € . '</div>';
}
Even though I dont know if there is another proper way with less code to do it, it worked for me