Improving the speed of a custom infinite scroll - php

I have a custom infinite scroll that is working perfectly but it's really slow. Here is the script that handles the ajax request:-
function ga_infinite_scroll() {//trigger this on infinite scroll
add_filter( 'woocommerce_get_price_html', 'ga_show_price' );//filter to fix price range
if(empty($_POST['search_term'] )){
$params = json_decode( stripslashes( $_POST['query'] ), true );
$params['post_status'] = 'publish';
$params['posts_per_page'] = get_option('posts_per_page');
$params['post_type'] = 'product';
$params['paged'] = $_POST['page'] + 1; // we need next page to be loaded
}
else{//search logic here
$search_query = json_decode( stripslashes( $_POST['search_posts'] ), true );
$search_query['post_status'] = 'publish';
$search_query['posts_per_page'] = get_option('posts_per_page');
$search_query['paged'] = $_POST['page'] + 1;
wc_set_loop_prop( 'total', $_POST['search_count'] );
$params = $search_query;
}
ob_start();
query_posts( $params);
if ( have_posts() ) {//product loop
if ( wc_get_loop_prop( 'total' ) ) {
while ( have_posts() ) {
the_post();
wc_get_template_part( 'content', 'product' );
}
}
}
$data = ob_get_clean();
die($data);
exit;
}
add_action( 'wp_ajax_ga_infinite_scroll', 'ga_infinite_scroll' );
add_action( 'wp_ajax_nopriv_ga_infinite_scroll', 'ga_infinite_scroll' );
Here's another post with my brief description of the problem Improving the performance of a custom developed scroll. Here is the code for ga_show_price.
function ga_show_price( $price ) {
global $post, $product, $reg_price_field_slug, $sale_price_field_slug, $user_currency, $wp_query,$wp_object_cache;
if( count($product->get_children()) !== 0 ) {
$variations = $product->get_children();
$regularPriceList = [];
$salePriceList = [];
$lowestPrice;
$salePrice;
// die("here");
if( $product->is_on_sale() ) {
// NOTE: ADD caching HERE!!
if( false === get_transient( 'sales_price' ) ) {
foreach( $variations as $variation ) {
array_push($salePriceList, get_post_meta( $variation, $reg_price_field_slug, true ) );
}
set_transient( 'sales_price', $salePriceList, 12 * HOUR_IN_SECONDS );
}
else{
$salePriceList = get_transient( 'sales_price');
}
$salePrice = min($salePriceList);
$price = add_proper_decimal($salePrice);
return get_woocommerce_currency_symbol() . $price . ' ' . $user_currency;
} else {
// NOTE: ADD caching HERE!!
if( false === get_transient( 'reg_price' ) ) {
foreach( $variations as $variation ) {
array_push($regularPriceList, get_post_meta( $variation, $reg_price_field_slug, true ) );
}
set_transient( 'reg_price', $regularPriceList, 12 * HOUR_IN_SECONDS );
}
else{
$regularPriceList = get_transient( 'reg_price');
}
$lowestPrice = min($regularPriceList);
$price = add_proper_decimal($lowestPrice);
return get_woocommerce_currency_symbol() . $price . ' ' . $user_currency;
}
} else {
$price = get_post_meta( $post->ID, $reg_price_field_slug, true );
$price = add_proper_decimal($price); // pr( $price );
if ( $price == '0.00' ) {
return 'Call for Price';
}
return get_woocommerce_currency_symbol() . $price . ' ' . $user_currency;
}
}
My javascript is here:-
jQuery(document).ready( function($) {
var url = window.location.origin + '/wp-admin/admin-ajax.php',
canBeLoaded=true,
bottomOffset = 2000; // the distance (in px) from the page bottom when you want to load more posts
$(window).scroll(function(){
var data = {
'action': 'ga_infinite_scroll',
'query': my_ajax_object.posts,
'page' : my_ajax_object.current_page,
//'search_results' : my_ajax_object.ga_search_results,
'search_count' : my_ajax_object.ga_search_count,
'search_posts': my_ajax_object.ga_search_posts,
'search_term' : my_ajax_object.ga_search_term,
'user_currency': my_ajax_object.user_currency,
'reg_price_slug': my_ajax_object.reg_price_field_slug
};
if( $(document).scrollTop() > ( $(document).height() - bottomOffset ) && canBeLoaded == true ){
$.ajax({//limit the ajax calls
url : url,
data:data,
type:'POST',
beforeSend: function( xhr ){
// you can also add your own preloader here
// you see, the AJAX call is in process, we shouldn't run it again until complete
//console.log(data.search_term);
$('#ajax-loader').show();
canBeLoaded = false;
},
success:function(data){
if( data ) {
$('#multiple-products .columns-3 .products ').find('li:last-of-type').after( data ); // where to insert posts
//console.log(url);
canBeLoaded = true; // the ajax is completed, now we can run it again
my_ajax_object.current_page++;
$('#ajax-loader').hide();
}
else{
$('#ajax-loader').html('End of products...').delay(1000).fadeOut();
return;
}
}
});
}
});
//setting if it's a search
});
Is there a way that i can use this woocommerce_get_price_html filter outside of the ajax request handling script(ga_infinite_scroll) as it's really costly in terms of speed to use it inside the ajax handling script? I tried using transients at the ga_show_price(). How to implement other types of caching here to increase the speed of the infinite scroll?

So using transients is probably the best "simple" answer here without doing some major rework. However there's a couple issues with your ga_show_price() function.
So you want to always minimise the amount of database calls or long lengthy functions you call from your code to make things faster.
Transients have GLOBAL names. So if you use something called sales_price for one product, as soon as you use it for another product it will still hold the value of the previous product. What you'll probably have to do is generate a unique name for all your transients. Something like: set_transient('price_'.$product->getSKU(), ...).
$variations = $product->get_children(); - You're loading the $variations variable with all the children of the product, which probably takes quite a while and involves quite a few db calls, then if you already have a transient for this product, the variations are never used!. Only run this line if you dont already have a cached value for the product.
A smaller issue, but you are calling get_transient twice every time you have a cached value. Once to check that it's not false, and then again to actually retrieve the value. Might seem like a small thing, but if you have 100+ products loading in, it adds up.
I like to do this with my transients:
$value = get_transient('something');
if ($value === false)
{
$value = some_long_calculation();
set_transient('something', $value, ...);
}
//Now use $value here.
Get even more aggressive with your caching. How often do items change from being on sale to not being on sale? Not more than once a day? Then just cache the entire function's calculation instead of first checking if it has a sales price or regular price.
A word of warning: Transients have a maximum length for their names and values so you cant store too much in them. Also they're designed for only storing a few values, not a price for every single product in your system.
If that is the case you're probably better off thinking about caching the value in a custom field for each product? You could attach hooks so that every time the product is updated, it updates the calculated price custom field automatically.

#Mikepote's suggestions for ga_price increased the speed but editing the main product loop based on unique transient increased speed more. I hereby attach my code:-
if( empty(get_transient('ga_loop_products_'.md5(serialize($params))))){ //using md5 and serialize(for 32digit) to assign a unique name to the given set of params
query_posts( $params);
ob_start();
add_filter( 'woocommerce_get_price_html', 'ga_show_price' );//filter to fix price range
if ( have_posts() ) {//product loop
if ( wc_get_loop_prop( 'total' ) ) {
while ( have_posts() ) {
the_post();
wc_get_template_part( 'content', 'product' );
}
}
}
$data = ob_get_clean();
// $ga_loop = get_transient('ga_loop_products_'.md5(serialize($params)));
set_transient( 'ga_loop_products_'.md5(serialize($params)), $data, 24 * 60 ); // 1 day cache
}
else{
$data= get_transient('ga_loop_products_'.md5(serialize($params)));
}
wp_reset_query();

Related

PHP to take data from WCFM Order meta, and push into AST plugin

While im pretty hopeless at php, i feel like im getting close on this one, i just cant quite lock it in
So we have 2 plugins in the mix here WCFM & AST
In WCFM, a vendor adds a tracking number and carrier this is store in the order meta data with a meta keys of
wcfm_tracking_code
AND
wcfm_tracking_url
i found this hook elesewhere that allows me to fire a set of things to happen, once the tracking has been submitted into the system
“wcfm_after_order_mark_shipped” (Parameters -> $order_id, $order_item_id, $tracking_code, $tracking_url)
so now i want to take the above 2 order meta datas and push them into another plugin called AST
They code they provide to push data into thier system is:
<?php if ( class_exists( 'WC_Advanced_Shipment_Tracking_Actions' ) ) {
$order_id = '123'; //Replace with your order id
$tracking_provider = 'USPS'; //Replace with your shipping provider
$tracking_number = '123123'; //Replace with your tracking number
$date_shipped = '2020-06-22'; ////Replace with your shipped date
$status_shipped = 1; // 0=no,1=shipped,2=partial shipped(if partial shipped order status is enabled)
$sku = 't-shirt,blue-jeans'; //the line item (product) SKU
$qty = '1,1'; //the line item (product) quantity
if ( function_exists( 'ast_insert_tracking_number' ) ) {
ast_insert_tracking_number( $order_id, $tracking_number, $tracking_provider, $date_shipped, $status_shipped, $sku, $qty );
}
}
SO using both codes provided, can anyone show me how i can take the metadata input from WCFM and push it into AST?
Ive hired someone on upwork, and they cant even figure it out, but it seems so straightforward.
This is the code we have,it pushes the data in but it comes in looking like this
Instead of this like it should
add_action( 'wcfm_after_order_mark_shipped', 'testwr', 99, 6 );
function testwr( $order_id, $order_item_id, $tracking_code, $tracking_url, $product_id, $wcfm_tracking_data ) {
$order = wc_get_order( $order_id );
$data['order_id'] = $order_id;
$data['tracking_provider'] = $tracking_url;
$data['wcfm_tracking_code'] = $wcfm_tracking_data['wcfm_tracking_code'];
$data['date_shipped'] = $order->order_date;
$data['status_shipped'] = 1;
extract($data);
foreach ($order->get_items() as $item) {
$product = wc_get_product($item->get_product_id());
$item_quantity[] = $item->get_quantity();
$item_sku[] = $product->get_sku();
}
if( $item_sku ) {
$data['sku'] = implode( ",",$item_sku );
}
$tracking_url = $tracking_url . "/" . $tracking_code;
$url = "<a href='".$tracking_url."'>test</a>";
$data['qty'] = implode( ",",$item_quantity );
$test = ast_insert_tracking_number( $order_id, $tracking_url, $tracking_url, $date_shipped, $status_shipped, $sku, $qty);
var_dump($test);
print_r($tracking_code);
print_r($data);
print_r($wcfm_tracking_data);
var_dump($order_item_id);
//ast_insert_tracking_number
die();
}
Kudos to anyone willing to give this a try and some time
Regards

Display coupon usage count and limit in WooCommerce thank you page

I want to display the remaining number of the coupon on the WooCommerce thankyou page
For example: You used the test coupon 3 times and there are 10 more left.
This is my code:
add_action('woocommerce_after_cart_table', 'coupon_count');
function coupon_count() {
global $woocommerce;
if ( ! empty( $woocommerce->cart->applied_coupons ) ) {
$my_coupon = $woocommerce->cart->get_coupons() ;
foreach($my_coupon as $coupon){
if ( $post = get_post( $coupon->id ) ) {
$counter = $coupon->get_usage_count();
echo "<span class='name-coupon'><b>Total usage for coupon </b><b>'</b><b>".$coupon->code."</b><b>'</b><b>: </b></span>";
echo "<span class='coupon-counter'>".($counter)."</span>";
}
}
}
}
But I have two problems:
1.With this code, only the number of times used is displayed and the remaining number is not displayed.
2.Replacing woocommerce_after_cart_table with woocommerce_thankyou on the thankyou page does not execute the code.
Instead of using the cart object, the order object is used on the thankyou page. So we're going to use that object instead
You can use get_usage_limit_per_user() to get coupon usage limit per customer (for a single customer)
OR use get_usage_limit() to get coupon usage limit.
So you get:
function action_woocommerce_thankyou( $order_id ) {
// Get $order object
$order = wc_get_order( $order_id );
foreach( $order->get_coupon_codes() as $coupon_code ) {
// Get the WC_Coupon object
$coupon = new WC_Coupon( $coupon_code );
// Get usage count
$count = $coupon->get_usage_count();
// Get coupon usage limit per customer
$limit = $coupon->get_usage_limit_per_user();
// OR use this instead, to get coupon usage limit.
// $limit = $coupon->get_usage_limit();
// NOT empty
if ( ! empty ( $count ) && ! empty ( $limit ) ) {
// Calculate remaining
$remaining = $limit - $count;
// Output
echo sprintf( '<span class="coupon-class">You used the <strong>%s</strong> coupon <strong>%d</strong> times and there are <strong>%d</strong> more left</span>', $coupon_code, $count, $remaining );
}
}
}
add_action( 'woocommerce_thankyou', 'action_woocommerce_thankyou', 10, 1 );
add_action('woocommerce_after_cart_table', 'coupon_count');
function coupon_count() {
global $woocommerce;
if ( ! empty( $woocommerce->cart->applied_coupons ) ) {
$my_coupon = $woocommerce->cart->get_coupons() ;
foreach($my_coupon as $coupon){
if ( $post = get_post( $coupon->id ) ) {
$counter = $coupon->get_usage_count();
$remaining = $coupon->get_usage_limit()-$counter;
echo "<span class='name-coupon'><b>Total usage for coupon </b><b>'</b><b>".$coupon->code."</b><b>'</b><b>: </b></span>";
echo "<span class='coupon-counter'>".($counter)."</span>";
echo "<span class='coupon-counter'> there are ".($remaining)." more left</span>";
}
}
}
}

Woocommerce Booking Add custom order item meta data

Looking for some help,
The aim is to capture several bits of data when a customer checks-out and for these to be stored as custom order item meta data.
Im looking to capture
Start-date
Start time
End date
End time
For each order, currently using WooCommerce booking and this data goes straight to the reservation area and doesn't appear as custom order item meta data.
Ive found a list of the hooks and filters whilst trying to create code myself, however I think im missing some bits along the way:
https://docs.woocommerce.com/document/bookings-action-and-filter-reference/
https://docs.woocommerce.com/document/tutorial-customising-checkout-fields-using-actions-and-filters/
https://wordpress.stackexchange.com/questions/215219/how-to-display-custom-field-in-woocommerce-orders-in-admin-panel
Ive tried to create this code to add these items to the custom order item meta data but without much success.
function my_custom_checkout_field_update_order_meta( $order_id ) {
if ( ! empty( $fields['woocommerce_bookings_after_booking_base_cost'] ) ) {
update_post_meta( $order_id, 'woocommerce_bookings_after_booking_base_cost', sanitize_text_field( $_POST['woocommerce_bookings_after_booking_base_cost'] ) );
}
}
add_action( 'woocommerce_admin_order_data_after_billing_address', 'my_custom_checkout_field_display_admin_order_meta', 10, 1 );
function my_custom_checkout_field_display_admin_order_meta($order){
echo '<p><strong>'.__('woocommerce_bookings_after_booking_base_cost').':</strong> <br/>' . get_post_meta( $order->get_id(), 'woocommerce_bookings_after_booking_base_cost', true ) . '</p>';
}
Im hoping someone has had this issue before and can share their knowledge or someone could point me in the right direction or help me with the coding, happy to admit I'm not the very best at coding but if someone could help break it down to me a little of where and what Im doing wrong, maybe what I'm doing correct as well, so I can understand how to improve.
Any help?
I also found this code, but it doesn't set the custom order meta data,
WOE_Bookings{
function __construct() {
add_filter('woe_get_order_product_fields',array($this,'add_product_fields') );
add_filter('woe_get_order_product_item',array($this,'fetch_booking') );
//set global filters
add_filter('woe_get_order_product_value_booking_status', array($this,'get_booking_field_status'), 10, 5 );
add_filter('woe_get_order_product_value_booking_start_date', array($this,'get_booking_field_start_date'), 10, 5 );
add_filter('woe_get_order_product_value_booking_start_time', array($this,'get_booking_field_start_time'), 10, 5 );
add_filter('woe_get_order_product_value_booking_end_date', array($this,'get_booking_field_end_date'), 10, 5 );
add_filter('woe_get_order_product_value_booking_end_time', array($this,'get_booking_field_end_time'), 10, 5 );
add_filter('woe_get_order_product_value_booking_resource', array($this,'get_booking_field_resource'), 10, 5 );
add_filter('woe_get_order_product_value_booking_persons_total', array($this,'get_booking_field_persons_total'), 10, 5 );
// add function for person types
if( class_exists("WC_Product_Booking_Data_Store_CPT") AND method_exists("WC_Product_Booking_Data_Store_CPT", "get_person_types_ids") ) {
$person_types_ids = WC_Product_Booking_Data_Store_CPT::get_person_types_ids();
foreach($person_types_ids as $type_id) {
add_filter('woe_get_order_product_value_booking_person_type_'.$type_id, function($value,$order, $item, $product, $item_meta) use ($type_id){
if (!$this->booking)
return $value;
$counters = $this->booking->get_person_counts();
return isset($counters[$type_id]) ? $counters[$type_id] : 0;
}, 10, 5 );
}
}
}
function add_product_fields($fields) {
$fields['booking_status'] = array('label'=>'Booking Status','colname'=>'Booking Status','checked'=>1);
$fields['booking_start_date'] = array('label'=>'Booking Start Date','colname'=>'Booking Start Date','checked'=>1);
$fields['booking_start_time'] = array('label'=>'Booking Start Time','colname'=>'Booking Start Time','checked'=>1);
$fields['booking_end_date'] = array('label'=>'Booking End Date','colname'=>'Booking End Date','checked'=>1);
$fields['booking_end_time'] = array('label'=>'Booking End Time','colname'=>'Booking End Time','checked'=>1);
$fields['booking_resource'] = array('label'=>'Booking Resource','colname'=>'Booking Resource','checked'=>1);
$fields['booking_persons_total'] = array('label'=>'Booking # of Persons','colname'=>'Booking Persons Total','checked'=>1,'segment'=>'cart');
// add person types as columns
if( class_exists("WC_Product_Booking_Data_Store_CPT") AND method_exists("WC_Product_Booking_Data_Store_CPT", "get_person_types_ids") ) {
$person_types_ids = WC_Product_Booking_Data_Store_CPT::get_person_types_ids();
foreach($person_types_ids as $type_id) {
$post = get_post($type_id);
if( $post )
$fields['booking_person_type_'.$type_id] = array('label'=>'Booking Persons - ' .$post->post_title,'colname'=>'Booking Persons - ' .$post->post_title,'checked'=>1);
}
}
return $fields;
}
// booking for item
function fetch_booking($item) {
global $wpdb;
$this->booking = false;
$booking_id = $wpdb->get_var( "SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key= '_booking_order_item_id' AND meta_value=" . intval( $item->get_id() ) );
if( $booking_id ) {
$this->booking = new WC_Booking($booking_id);
}
return $item;
}
function get_booking_field_status($value,$order, $item, $product, $item_meta) {
return $this->booking ? $this->booking->get_status() : $value;
}
function get_booking_field_start_date($value,$order, $item, $product, $item_meta) {
return $this->booking ? date_i18n( wc_date_format(), $this->booking->start) : $value;
}
function get_booking_field_start_time($value,$order, $item, $product, $item_meta) {
return $this->booking ? date_i18n( wc_time_format(), $this->booking->start) : $value;
}
function get_booking_field_end_date($value,$order, $item, $product, $item_meta) {
return $this->booking ? date_i18n( wc_date_format(), $this->booking->end) : $value;
}
function get_booking_field_end_time($value,$order, $item, $product, $item_meta) {
return $this->booking ? date_i18n( wc_time_format(), $this->booking->end) : $value;
}
function get_booking_field_resource($value,$order, $item, $product, $item_meta) {
if (!$this->booking)
return $value;
$resource = $this->booking->get_resource();
return $resource ? $resource->get_name() : $value;
}
function get_booking_field_persons_total($value,$order, $item, $product,$item_meta) {
return $this->booking ? $this->booking->get_persons_total() : $value;
}
}
new WOE_Bookings();
add_action( 'woocommerce_add_order_item_meta', 'add_order_item_meta' , 10, 2);
function add_order_item_meta ( $item_id, $values ) {
if ( isset( $values [ 'booking_start_date' ] ) ) {
$custom_data = $values [ 'booking_start_date' ];
wc_add_order_item_meta( $item_id, ‘date',
$custom_data['booking_start_date'] );
}
}
You don't need to add any custom meta data to the order, as you can retrieve it easily (as you are expecting).
You will see in the following code example that displays the start date, the start time, the End date and the End time in admin single orders below shipping address:
add_action( 'woocommerce_admin_order_data_after_shipping_address', 'display_some_booking_data_to_admin_orders', 10, 1 );
function display_some_booking_data_to_admin_orders( $order ){
// Retreive the booking Ids that belong to an order
$bookings_ids = get_posts( array(
'posts_per_page' => -1,
'post_type' => 'wc_booking', // booking post type
'post_parent' => $order->get_id(),
'post_status' => 'all',
'fields' => 'ids',
) );
if( ! empty($bookings_ids) ) {
echo '<div class="booking-items">';
// Loop through Bookings for the current order
foreach ( $bookings_ids as $booking_id ) {
// Get an instance of the WC_Booking Object
$booking = new WC_Booking( $booking_id );
// Get the related instance of the order Item Object
$item = $order->get_item($booking->order_item_id);
$name = $item->get_name(); // Get product name
// Start date and time
$start_datetime = $booking->get_start_date();
$start_dt_array = explode(', ', $start_datetime);
$start_date = reset($start_dt_array); // Start date
$start_time = end($start_dt_array); // Start time
// End date and time
$end_datetime = $booking->get_end_date();
$end_dt_array = explode(', ', $end_datetime);
$end_date = reset($end_dt_array); // End date
$end_time = end($end_dt_array); // End time
echo '<table style="border:solid 1px #eee; margin-bottom:12px;">
<thead style="background-color: #eee;">
<tr><td><strong>' . __("Booking") . ':</strong></td><td> ' . $name . ' <em><small>(' . $booking_id . ')</small></em></td></tr>
</thead>
<tbody>
<tr><td><strong>' . __("Start date") . ':</strong></td><td>' . $start_datetime . '</td></tr>
<tr><td><strong>' . __("End date") . ':</strong></td><td>' . $end_datetime . '</td></tr>
</tbody>
</table>';
}
echo '</div>';
}
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
Addition for Exporting order data
Save booking data as custom order meta data (only handle one booking):
add_action( 'woocommerce_checkout_create_order', 'display_some_booking_data_to_admin_orders', 10, 1 );
function display_some_booking_data_to_admin_orders( $order ){
$cart_items = WC()->cart->get_cart(); // Get cart items
$cart_item = reset($cart_item); // First cart item
if( isset($cart_item['booking']) && ! empty($cart_item['booking']) ) {
$booking_id = $cart_item['booking']['_booking_id'];
$order->update_meta_data( '_booking_id', $booking_id ); // Save booking Id
// Get an instance of the WC_Booking Object
$booking = new WC_Booking( $cart_item['booking']['_booking_id'] );
// Start date and time
$start_datetime = $booking->get_start_date();
$start_dt_array = explode(', ', $start_datetime);
$order->update_meta_data( 'booking_start_datetime', $start_datetime ); // Save start date and time
$order->update_meta_data( 'booking_start_date', reset($start_dt_array) ); // Save start date
$order->update_meta_data( 'booking_start_time', end($start_dt_array) ); // Save start time
// End date and time
$end_datetime = $booking->get_end_date();
$end_dt_array = explode(', ', $end_datetime);
$order->update_meta_data( 'booking_end_datetime', $end_datetime ); // Save end date and time
$order->update_meta_data( 'booking_end_date', reset($end_dt_array) ); // Save end date
$order->update_meta_data( 'booking_end_time', end($end_dt_array) ); // Save end time
// Cost and quantiity
$order->update_meta_data( 'booking_cost', $cart_item['booking']['_cost'] ); // Save cost
$order->update_meta_data( 'booking_qty', $cart_item['booking']['_qty'] ); // Save quantity
}
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
This will allow you to export the booking data with your plugin.
Please, use this solution to filter by start date
https://wordpress.org/support/topic/filter-by-bookings-start-date/#post-12908495
if you still need help - please, submit ticket to https://algolplus.freshdesk.com/
thanks, Alex
(support of Advanced Order Export For WooCommerce)

Display custom product image on WooCommerce email notifications

I am trying to modify the product images in the auto generated WooCommerce order e-mails. Note, I am trying to do this with hooks, rather than creating a modified e-mail template.
To start, I have a hidden input on the single product page. I have some JS that sets the ID of the image, which is based on the product's colour (a custom attribute I made with ACF).
<input class="js-col-img-id" type="hidden" name="product-col-img-id" value="">
For reference, here's what I've done to get this to work on the cart page:
// Add custom attributes to cart item data
add_action('woocommerce_add_cart_item_data', 'jwd_add_custom_attr_to_cart', 10, 3);
function jwd_add_custom_attr_to_cart($cart_item_data, $product_id, $variation_id) {
$attrs = array(...,'product-col-img-id');
foreach ($attrs as $attr) {
$san_attr = filter_input( INPUT_POST, $attr );
if ( !empty( $san_attr ) ) {
$cart_item_data[$attr] = $san_attr;
}
}
return $cart_item_data;
}
// This function sets the image ID in the cart, and it displays the image properly
add_action( 'woocommerce_before_calculate_totals', 'bwa_new_price' );
function bwa_new_price($cart) {
// ...
$cart_items = $cart->get_cart();
foreach($cart_items as $item) {
$data = $item['data'];
// product-col-img-id is sent through the hidden input when the add to cart form is submitted
$colour_img_ID = $item['product-col-img-id'] ?? false;
if ($colour_img_ID) {
$data->set_image_id($colour_img_ID);
}
}
}
// Add custom attributes to order
add_action( 'woocommerce_checkout_create_order_line_item', 'jwd_add_attr_to_order_items', 10, 4 );
function jwd_add_attr_to_order_items( $item, $cart_item_key, $values, $order ) {
if ( !empty( $values['product-colour'] ) ) {
$item->add_meta_data( 'Colour', $values['product-colour'] );
}
// I tried replicating this for the img ID. It does display it in the var dump mentioned below, and it does display it as an actual label in the e-mail (which I'd want to get rid of). The difficulty then becomes reading the ID through code and setting the product to display that image
if ( !empty( $values['product-col-img-id'] ) ) {
$item->add_meta_data( 'col_img_id', $values['product-col-img-id'] );
}
}
That works fine, but I'm having difficulty replicating this in the order e-mail. So far I've come across the following. This shows images in the e-mail, but it's the product's featured image. Of note is the "show_image" key being set to true.
// Add prod IMG to e-mails
function jwd_add_images_woocommerce_emails( $output, $order ) {
// set a flag so we don't recursively call this filter
static $run = 0;
// if we've already run this filter, bail out
if ( $run ) {
return $output;
}
$args = array(
'show_image' => true,
//'image_size' => array( 300, 300 ),
'image_size' => 'full',
);
// increment our flag so we don't run again
$run++;
// if first run, give WooComm our updated table
return wc_get_email_order_items($order, $args);
}
add_filter( 'woocommerce_email_order_items_table', 'jwd_add_images_woocommerce_emails', 10, 2 );
To modify the image, I found this filter, but I'm just having a difficult time figuring out what I need to do to replicate what I've done on the cart page. The closest I've got is having $item_data work, and I do see the product-col-img-id key in the var dump, though it's in a protected object, which I'm not sure how to access. Even if I could access, I'm not sure how I could even set the image from here.
add_filter('woocommerce_order_item_thumbnail', 'jwd_add_colour_img', 10, 2);
function jwd_add_colour_img($image, $item) {
$item_id = $item->get_id();
//var_dump($item->get_current_data());
$product = $item->get_product();
$product_id = $item->get_product_id();
$item_data = $item->get_data();
$col_img_id = false;
$item->set_image_id(1);
foreach ($item_data as $key => $item) {
//current_data
var_dump($item);
//echo "<br><br>";
$col_img_id = $item['product-col-img-id'] ?? false;
if ($col_img_id) {
break;
}
}
//var_dump($col_img_id);
//$item_id = $item->get_product_id();
//var_dump(wc_get_order_item_meta( $item_id, 'col_img_id', true ) );
return $image;
}
To get WooCommerce custom meta data, you can use WC_Data method get_meta().
Try the following instead to display your custom image on email notifications:
// Save custom image ID as order item meta
add_action( 'woocommerce_checkout_create_order_line_item', 'save_custom_image_id_to_order_item', 10, 4 );
function save_custom_image_id_to_order_item( $item, $cart_item_key, $values, $order ) {
if ( isset($values['product-colour']) && ! empty($values['product-colour']) ) {
$item->add_meta_data('Colour', $values['product-colour'] );
}
if ( isset($values['product-col-img-id']) && ! empty($values['product-col-img-id']) ) {
$item->add_meta_data('_col_img_id', $values['product-col-img-id'] );
}
}
// (optional) Force display item image on emails
add_filter( 'woocommerce_email_order_items_args', 'show_image_on_email_notifications' );
function show_image_on_email_notifications( $args ) {
$args['show_image'] = true;
return $args;
}
// Display custom image on emails
add_filter( 'woocommerce_order_item_thumbnail', 'display_email_order_item_custom_image', 10, 2 );
function display_email_order_item_custom_image( $image, $item ) {
// Only on email notifications
if( is_wc_endpoint_url() )
return $image;
$image_id = $item->get_meta('_col_img_id');
if( $image_id ) {
$image = wp_get_attachment_image( $image_id, array( 32, 32 ), false, array() );
}
return $image;
}
Code goes in functions.php file of the active child theme (or active theme). It should work.

Improving the performance of a custom developed scroll

I am trying to improve the speed of my infinite scroll code. Here is my ajax requet handling script
function ga_infinite_scroll() {//trigger this on infinite scroll
add_filter( 'woocommerce_get_price_html', 'ga_show_price' );//filter to fix price range
if(empty($_POST['search_term'] )){
$params = json_decode( stripslashes( $_POST['query'] ), true );
$params['post_status'] = 'publish';
$params['posts_per_page'] = get_option('posts_per_page');
$params['post_type'] = 'product';
$params['paged'] = $_POST['page'] + 1; // we need next page to be loaded
}
else{//search logic here
$search_query = json_decode( stripslashes( $_POST['search_posts'] ), true );
$search_query['post_status'] = 'publish';
$search_query['posts_per_page'] = get_option('posts_per_page');
$search_query['paged'] = $_POST['page'] + 1;
wc_set_loop_prop( 'total', $_POST['search_count'] );
$params = $search_query;
}
ob_start();
query_posts( $params);
if ( have_posts() ) {//product loop
if ( wc_get_loop_prop( 'total' ) ) {
while ( have_posts() ) {
the_post();
wc_get_template_part( 'content', 'product' );
}
}
}
$data = ob_get_clean();
die($data);
exit;
}
add_action( 'wp_ajax_ga_infinite_scroll', 'ga_infinite_scroll' );
add_action( 'wp_ajax_nopriv_ga_infinite_scroll', 'ga_infinite_scroll' );
Here is my javascript:-
jQuery(document).ready( function($) {
var url = window.location.origin + '/wp-admin/admin-ajax.php',
canBeLoaded=true,
bottomOffset = 2000; // the distance (in px) from the page bottom when you want to load more posts
$(window).scroll(function(){
var data = {
'action': 'ga_infinite_scroll',
'query': my_ajax_object.posts,
'page' : my_ajax_object.current_page,
//'search_results' : my_ajax_object.ga_search_results,
'search_count' : my_ajax_object.ga_search_count,
'search_posts': my_ajax_object.ga_search_posts,
'search_term' : my_ajax_object.ga_search_term,
'user_currency': my_ajax_object.user_currency,
'reg_price_slug': my_ajax_object.reg_price_field_slug
};
if( $(document).scrollTop() > ( $(document).height() - bottomOffset ) && canBeLoaded == true ){
$.ajax({//limit the ajax calls
url : url,
data:data,
type:'POST',
beforeSend: function( xhr ){
// you can also add your own preloader here
// you see, the AJAX call is in process, we shouldn't run it again until complete
//console.log(data.search_term);
$('#ajax-loader').show();
canBeLoaded = false;
},
success:function(data){
if( data ) {
$('#multiple-products .columns-3 .products ').find('li:last-of-type').after( data ); // where to insert posts
//console.log(url);
canBeLoaded = true; // the ajax is completed, now we can run it again
my_ajax_object.current_page++;
$('#ajax-loader').hide();
}
else{
$('#ajax-loader').html('End of products...').delay(1000).fadeOut();
return;
}
}
});
}
});
//setting if it's a search
});
I'm wondering if it's a good idea to use query_posts and I have this filter that costly in terms of speed but if I don't use it, I end up seeing a price range like this $15-$25 like that.Any idea about how to handle this situation and to see the code of ga_show_price and my enqueued script, I've added it here Improving the speed of a custom infinite scroll and also any suggestions on improving my javascript?Thanks
You can get related products using the following:
$array = wc_get_related_products( $product_id, $limit, $exclude_ids );

Categories