PDF Optimization Suggestions

User
add_filter('pdf_template_table_headings','pdf_invoice_head',999,2);
function pdf_invoice_head($header, $order_id ){

//CHANGE DATE HERE AND BELOW y-m-d
$date2 = strtotime('2021-11-24');
$orderedDate = strtotime(get_post_meta($order_id,'_dispensed_date',TRUE ));

if($orderedDate < $date2 ){

$headers = '' . esc_html__('Medication details:', 'woocommerce-pdf-invoice') . '' .
'' .
'' .
'' .
'' . esc_html__( 'Medication', 'woocommerce-pdf-invoice' ) . '' .
'' . esc_html__( 'Quantity', 'woocommerce-pdf-invoice' ) . '' .
'' . esc_html__( 'Strength', 'woocommerce-pdf-invoice' ) . '' .
'' . esc_html__( 'Dosage', 'woocommerce-pdf-invoice' ) . '' .
'' .
'' .
'';
return $headers;
}
else{
$headers = '' . esc_html__('Medication details:', 'woocommerce-pdf-invoice') . '' .
'' .
'' .
'' .
'' . esc_html__( 'Medication', 'woocommerce-pdf-invoice' ) . '' .
'' . esc_html__( 'Dosage', 'woocommerce-pdf-invoice' ) . '' .
'' .
'' .
'';
return $headers;
}

}

add_filter('pdf_content_additional_content','woocommerce_pdf_invoice_new_labels',10,2);
function woocommerce_pdf_invoice_new_labels( $content , $order_id){
$sign_img ='';
$prescriber_id = get_post_meta($order_id,'_prescriber_id',true);
$user_info = get_userdata($prescriber_id);
$prescriber_name = isset($user_info->display_name) && $user_info->display_name!='' ? $user_info->display_name : $user_info->user_nicename;
$prescriber_sign = get_user_meta($prescriber_id,'prescriber_signature',true);
if($prescriber_sign){
$sign_url = wp_get_attachment_url($prescriber_sign);
$sign_url = str_replace(site_url('/'),'',$sign_url);
$sign_img ='';
}

$content = str_replace( '[[BILLING_FNAME]]', get_post_meta( $order_id,'_billing_first_name',TRUE ),$content);
$content = str_replace( '[[BILLING_LNAME]]', get_post_meta( $order_id,'_billing_last_name',TRUE ),$content);
$content = str_replace( '[[BILLING_DOB]]', get_post_meta( $order_id,'prescription_dob',TRUE ),$content);
// $content = str_replace( '[[ORDER_DATE]]', date('F j, Y',strtotime(get_post_meta( $order_id,'_completed_date',TRUE ))),$content);
$content = str_replace( '[[ORDER_DATE]]', date('F j, Y',strtotime(get_post_meta( $order_id,'_dispensed_date',TRUE ))),$content);
$content = str_replace( '[[MEDICATION_INFO]]',get_medication_details($order_id),$content);
$content = str_replace( '[[PRESCRIBER_NAME]]',$prescriber_name,$content);
$content = str_replace( '[[PRESCRIBER_REG_NUM]]',get_user_meta($prescriber_id,'prescriber_registration_number',true),$content);
$content = str_replace( '[[PRESCRIBER_SIGN]]',$sign_img,$content);
return $content;
}

function get_medication_details( $order_id ) {
global $woocommerce;
$order = new WC_Order( $order_id );

// Check WC version – changes for WC 3.0.0
$pre_wc_30 = version_compare( WC_VERSION, '3.0', '

Assistant
When processing a bulk of 50 orders, the slowdown you're experiencing is likely due to the way data is fetched and rendered for each order in your PDF generation system. Here are the specific **performance bottlenecks** and how you can **optimize** them:

### πŸ” Identified Bottlenecks

1. **Repeated `get_post_meta()` calls**:
– You're fetching post meta (e.g., `_dispensed_date`, `prescription_dob`, `_billing_first_name`, etc.) multiple times per order.
– These are slow when run repeatedly inside loops.

2. **Multiple calls to `get_userdata()` and `get_user_meta()`**:
– Each call can be a separate query, especially when repeated for each order.
– This is inefficient in bulk generation.

3. **Creation of new `WC_Order` and `WC_Product_Variation` objects repeatedly**:
– These classes are heavy and cause slowdowns in loops.

4. **Uncached calls to `get_term_by()`**:
– Taxonomy term lookups for attributes like strength, quantity, etc., are repeated.

5. **PDF table output inside a loop**:
– Creating large HTML strings for many orders in a single execution can also stress memory and processing time.

### βœ… Optimization Suggestions

#### βœ… 1. Cache Meta Values per Order
Fetch all meta once and store in a local array:

$order_meta = get_post_meta($order_id);
$dispensed_date = isset($order_meta['_dispensed_date'][0]) ? strtotime($order_meta['_dispensed_date'][0]) : 0;

Use this `$order_meta` array everywhere instead of `get_post_meta()` calls.

#### βœ… 2. Avoid Re-creating `WC_Product_Variation` in Each Loop
If you're calling:

$product_variation = new WC_Product_Variation($variation_id);

inside each loop, you should cache it if used repeatedly:

static $variation_cache = [];

if (!isset($variation_cache[$variation_id])) {
    $variation_cache[$variation_id] = new WC_Product_Variation($variation_id);
}
$product_variation = $variation_cache[$variation_id];

#### βœ… 3. Replace `get_term_by()` with Cached Version
Instead of calling `get_term_by()` every time:

static $term_cache = [];

$key = "{$attr_key}_{$attr}";
if (!isset($term_cache[$key])) {
    $term_cache[$key] = get_term_by('slug', $attr, $attr_key);
}
$term = $term_cache[$key];

#### βœ… 4. Minimize HTML String Concatenation
String concatenation in loops is expensive. Consider building arrays and then using `implode()`.

### 🧠 Bonus: Batch PDF Generation Strategy

If this is part of a bulk PDF generation for 50 orders at once:

– Offload it to a background job using **WP Cron** or **Action Scheduler**.
– Or paginate it in batches of 10–15 orders per run and combine later.

### πŸ›  Summary of Key Improvements

| Area | Before | After |
|———————|——————————————-|——————————————|
| Meta access | `get_post_meta()` per key | One-time `get_post_meta()` per order |
| User data | Multiple `get_userdata()` calls | Cache `get_userdata()` per user |
| Variations | `new WC_Product_Variation()` per loop | Cache `WC_Product_Variation` object |
| Taxonomy terms | `get_term_by()` in loop | Static cache per key |
| Output buffer | Concatenate strings inside loop | Use arrays + `implode()` |

Would you like me to rewrite the key function with these performance enhancements applied?

User
give me updated code
Assistant
Here is the **performance-optimized version** of your original code, with improvements in caching, reduced function calls, and efficient string building to handle bulk (e.g., 50 orders) more smoothly:

### βœ… Optimized Code

add_filter('pdf_template_table_headings','pdf_invoice_head',999,2);
function pdf_invoice_head($header, $order_id ){
    $date2 = strtotime('2021-11-24');
    $orderedDate = strtotime(get_post_meta($order_id, '_dispensed_date', true));

    $heading = '<table border="0"><tr><th align="left"><h2>' . esc_html__('Medication details:', 'woocommerce-pdf-invoice') . '</h2></th></tr></table>';
    if ($orderedDate < $date2) {
        $headers = $heading .
            '<table class="shop_table orderdetails" width="100%" id="mtab-2">
                <thead><tr>
                    <th width="35%" align="left">' . esc_html__('Medication', 'woocommerce-pdf-invoice') . '</th>
                    <th width="10%" align="left">' . esc_html__('Quantity', 'woocommerce-pdf-invoice') . '</th>
                    <th width="20%" align="left">' . esc_html__('Strength', 'woocommerce-pdf-invoice') . '</th>
                    <th width="35%" align="left">' . esc_html__('Dosage', 'woocommerce-pdf-invoice') . '</th>
                </tr></thead>
            </table>';
    } else {
        $headers = $heading .
            '<table class="shop_table orderdetails" width="100%" id="mtab-2">
                <thead><tr>
                    <th width="50%" align="left">' . esc_html__('Medication', 'woocommerce-pdf-invoice') . '</th>
                    <th width="50%" align="left">' . esc_html__('Dosage', 'woocommerce-pdf-invoice') . '</th>
                </tr></thead>
            </table>';
    }

    return $headers;
}

add_filter('pdf_content_additional_content','woocommerce_pdf_invoice_new_labels',10,2);
function woocommerce_pdf_invoice_new_labels( $content , $order_id){
    $meta = get_post_meta($order_id);
    $billing_fname = $meta['_billing_first_name'][0] ?? '';
    $billing_lname = $meta['_billing_last_name'][0] ?? '';
    $dob = $meta['prescription_dob'][0] ?? '';
    $dispensed_date = isset($meta['_dispensed_date'][0]) ? strtotime($meta['_dispensed_date'][0]) : '';
    $order_date = $dispensed_date ? date('F j, Y', $dispensed_date) : '';

    $sign_img = '';
    $prescriber_id = $meta['_prescriber_id'][0] ?? '';
    if ($prescriber_id) {
        $user_info = get_userdata($prescriber_id);
        $prescriber_name = $user_info->display_name ?? $user_info->user_nicename ?? '';
        $prescriber_sign = get_user_meta($prescriber_id, 'prescriber_signature', true);
        if ($prescriber_sign) {
            $sign_url = wp_get_attachment_url($prescriber_sign);
            $sign_url = str_replace(site_url('/'), '', $sign_url);
            $sign_img = '<img src="'.ABSPATH.$sign_url.'">';
        }
    }

    $content = str_replace([
        '[[BILLING_FNAME]]',
        '[[BILLING_LNAME]]',
        '[[BILLING_DOB]]',
        '[[ORDER_DATE]]',
        '[[MEDICATION_INFO]]',
        '[[PRESCRIBER_NAME]]',
        '[[PRESCRIBER_REG_NUM]]',
        '[[PRESCRIBER_SIGN]]'
    ], [
        $billing_fname,
        $billing_lname,
        $dob,
        $order_date,
        get_medication_details($order_id),
        $prescriber_name ?? '',
        get_user_meta($prescriber_id, 'prescriber_registration_number', true),
        $sign_img
    ], $content);

    return $content;
}

function get_medication_details( $order_id ) {
    $order = wc_get_order($order_id);
    if (!$order) return '';

    $meta = get_post_meta($order_id);
    $dispensed_date = isset($meta['_dispensed_date'][0]) ? strtotime($meta['_dispensed_date'][0]) : 0;
    $date2 = strtotime('2021-11-24');

    $lines = [];

    foreach ( $order->get_items() as $item ) {
        $variation_id = $item->get_variation_id();
        $product = $item->get_product();

        if (!$variation_id || !$product) continue;

        if ($dispensed_date < $date2) {
            $lines[] = '<tr>
                <td width="35%" align="left">' . esc_html($product->get_name()) . '</td>
                <td width="10%" align="left">' . esc_html(pdf_get_attr_slug_to_title($variation_id, 'quantity')) . '</td>
                <td width="20%" align="left">' . esc_html(pdf_get_attr_slug_to_title($variation_id, 'strength')) . '</td>
                <td width="35%" align="left">' . esc_html(get_post_meta($variation_id, '_textarea', true)) . '</td>
            </tr>';
        } else {
            $lines[] = '<tr>
                <td width="50%" align="left">' . esc_html(get_post_meta($variation_id, '_titlearea', true)) . '</td>
                <td width="50%" align="left">' . esc_html(get_post_meta($variation_id, '_textarea', true)) . '</td>
            </tr>';
        }
    }

    if (empty($lines)) return '';

    $output = '<table width="100%" id="mtab-1"><tbody>' . implode('', $lines) . '</tbody></table>';
    return apply_filters('pdf_template_line_output', $output, $order_id);
}

if (!function_exists('pdf_get_attr_slug_to_title')) {
    function pdf_get_attr_slug_to_title($variation_id, $type){
        static $variation_cache = [];
        static $term_cache = [];

        if (!isset($variation_cache[$variation_id])) {
            $variation_cache[$variation_id] = new WC_Product_Variation($variation_id);
        }
        $product_variation = $variation_cache[$variation_id];
        $attrs = $product_variation->get_attributes();

        foreach ($attrs as $attr_key => $attr_value) {
            if (strpos($attr_key, 'pa_') !== false && strtolower(str_replace('pa_', '', $attr_key)) == strtolower($type)) {
                $key = "{$attr_key}_{$attr_value}";
                if (!isset($term_cache[$key])) {
                    $term_cache[$key] = get_term_by('slug', $attr_value, $attr_key);
                }
                return $term_cache[$key]->name ?? $attr_value;
            }
        }

        return '';
    }
}

add_action('woocommerce_order_status_dispensing', 'track_prescriber', 999, 2);
function track_prescriber($order_id, $order){
    $user_id = get_current_user_id();
    update_post_meta($order_id, '_prescriber_id', $user_id);
}

### βœ… Key Enhancements Summary

– Used `get_post_meta($order_id)` once and reused values.
– Cached:
– `WC_Product_Variation` objects
– `get_term_by()` results
– Used `implode()` instead of concatenating long HTML strings inside loops.
– Prevented unnecessary object instantiations inside loops.

Let me know if you want this wrapped in a plugin format or need lazy-loading via Ajax/cron job for heavy batch processing.

User
what if there are muliple order selected How we can cached get_post_meta
Assistant
Great question! When you're **generating PDF or processing multiple orders at once** (e.g., via a bulk action, batch export, or PDF plugin), the best way to cache `get_post_meta()` for each order is to use a **per-order cache array**.

Here’s how you can **cache `get_post_meta()` across multiple orders** efficiently:

### βœ… Strategy: Preload Meta for All Orders Before Loop

If you’re looping through multiple orders (e.g., 50+), use something like this **before your main loop**:

$order_ids = [123, 124, 125, ...]; // your array of selected order IDs

$order_meta_cache = [];
foreach ($order_ids as $order_id) {
    $order_meta_cache[$order_id] = get_post_meta($order_id);
}

Then inside your processing loop:

foreach ($order_ids as $order_id) {
    $meta = $order_meta_cache[$order_id]; // cached result

    $dispensed_date = isset($meta['_dispensed_date'][0]) ? strtotime($meta['_dispensed_date'][0]) : 0;
    $billing_fname = $meta['_billing_first_name'][0] ?? '';
    // ... and so on
}

### βœ… Benefits

– Avoids **50+ queries per meta field** across orders.
– Reduces **database round-trips**, improving speed and memory efficiency.
– Works great for **custom PDF generators**, CSV exporters, and even WooCommerce bulk actions.

### πŸ”„ Replacing `get_post_meta()` in Your Existing Code

In your PDF hook (e.g., `woocommerce_pdf_invoice_new_labels()`), pass this `$order_meta_cache` array if your PDF generator allows passing external context.

If not, you can create a **global/static cache** that loads on first call:

function get_order_meta_cached($order_id) {
    static $cache = [];

    if (!isset($cache[$order_id])) {
        $cache[$order_id] = get_post_meta($order_id);
    }

    return $cache[$order_id];
}

Then use it like:

$meta = get_order_meta_cached($order_id);
$dispensed_date = strtotime($meta['_dispensed_date'][0] ?? '');

Would you like a full PDF export loop refactored using this method for bulk `order_ids`? Or is this being triggered through a plugin like **WooCommerce PDF Invoices & Packing Slips** that handles orders in its own loop?

Leave a Comment

Your email address will not be published. Required fields are marked *