phpwordpresswoocommercemetadataorders

update_post_meta() not working on WooCommerce checkout when submmitting an order


I've created a bespoke plugin for a client of mine but I am running into some problems. I tested the plugin on my own sandbox site before sending it to the client and it worked perfectly. However, on their site, which is built differently, I am running into a couple of problems.

One of the files runs functions using common Woocommerce hooks.

<?php
if (!defined('ABSPATH')) {
    exit;
}

class WC_PM_Checkout_Fields {

    public function __construct() {
        add_action('woocommerce_after_order_notes', array($this, 'add_patient_dropdown_and_custom_fields_to_checkout'), 999);
        add_action('woocommerce_checkout_update_order_meta', array($this, 'save_or_create_patient_on_checkout'), 999);
        add_action('woocommerce_thankyou', array($this, 'display_patient_information_on_thank_you_page'), 999);
        add_action('woocommerce_email_order_meta', array($this, 'add_patient_info_to_order_email'), 999, 3);
        add_action('woocommerce_admin_order_data_after_billing_address', array($this, 'display_patient_fields_in_admin_order'), 999, 1);
    }

    public function add_patient_dropdown_and_custom_fields_to_checkout($checkout) {

        if( !is_checkout() ) {
            return;
        }

        $args = array(
            'post_type'      => 'patient',
            'author'         => get_current_user_id(),
            'posts_per_page' => -1,
        );

        $patients = new WP_Query($args);

        echo '<div id="patient_dropdown"><h3>' . __('Select Existing Patient') . '</h3>';
        echo '<select name="selected_patient_id" id="selected_patient_id">';
        echo '<option value="">' . __('Select a patient or enter new details below') . '</option>';

        if ($patients->have_posts()) :
            while ($patients->have_posts()) : $patients->the_post();
                echo '<option value="' . get_the_ID() . '">' . get_the_title() . '</option>';
            endwhile;
            wp_reset_postdata();
        endif;

        echo '</select></div>';

        echo '<div id="custom_patient_checkout_fields"><h3>' . __('Or Enter New Patient Information') . '</h3>';

        if (function_exists('woocommerce_form_field')) {
            // First Name field
            woocommerce_form_field('wcpm_patient_first_name', array(
                'type'          => 'text',
                'class'         => array('form-row-wide'),
                'label'         => __('Patient First Name'),
                'required'      => true,
                'placeholder'   => __('Enter patient first name'),
            ), $checkout->get_value('wcpm_patient_first_name'));

            // Last Name field
            woocommerce_form_field('wcpm_patient_last_name', array(
                'type'          => 'text',
                'class'         => array('form-row-wide'),
                'label'         => __('Patient Last Name'),
                'required'      => true,
                'placeholder'   => __('Enter patient last name'),
            ), $checkout->get_value('wcpm_patient_last_name'));

            // Date of Birth field
            woocommerce_form_field('wcpm_patient_dob', array(
                'type'          => 'text',
                'class'         => array('form-row-wide'),
                'label'         => __('Date of Birth'),
                'required'      => true,
                'placeholder'   => __('Select date of birth'),
                'input_class'   => array('datepicker'),
            ), $checkout->get_value('wcpm_patient_dob'));

            // Prescription Dosage field
            woocommerce_form_field('wcpm_patient_dosage', array(
                'type'          => 'text',
                'class'         => array('form-row-wide'),
                'label'         => __('Prescription Dosage'),
                'required'      => true,
                'placeholder'   => __('Enter prescription dosage'),
            ), $checkout->get_value('wcpm_patient_dosage'));

            // Prescription Date field
            woocommerce_form_field('wcpm_patient_prescription_date', array(
                'type'          => 'text',
                'class'         => array('form-row-wide'),
                'label'         => __('Prescription Date'),
                'required'      => true,
                'placeholder'   => __('Select prescription date'),
                'input_class'   => array('datepicker'),
            ), $checkout->get_value('wcpm_patient_prescription_date'));
        }
        echo '</div>';
    }

    public function save_or_create_patient_on_checkout($order_id) {
        $selected_patient_id = !empty($_POST['selected_patient_id']) ? intval($_POST['selected_patient_id']) : null;
        $patient_first_name = sanitize_text_field($_POST['wcpm_patient_first_name']);
        $patient_last_name = sanitize_text_field($_POST['wcpm_patient_last_name']);
        $patient_dob = !empty($_POST['wcpm_patient_dob']) ? date('Y-m-d', strtotime(str_replace('/', '-', $_POST['wcpm_patient_dob']))) : '';
        $patient_dosage = sanitize_text_field($_POST['wcpm_patient_dosage']);
        $patient_prescription_date = !empty($_POST['wcpm_patient_prescription_date']) ? date('Y-m-d', strtotime(str_replace('/', '-', $_POST['wcpm_patient_prescription_date']))) : '';

        if ($selected_patient_id) {
            // An existing patient was selected
            $patient_id = $selected_patient_id;
        } else {
            // Create a new patient post
            $patient_title = $patient_first_name . ' ' . $patient_last_name;
            $current_user_id = get_current_user_id();
    
            $existing_patient = get_posts(array(
                'post_type'  => 'patient',
                'author'     => $current_user_id,
                'post_status'=> 'publish',
                'meta_query' => array(
                    array(
                        'key'     => 'wcpm_patient_first_name',
                        'value'   => ucwords($patient_first_name),
                        'compare' => '='
                    ),
                    array(
                        'key'     => 'wcpm_patient_last_name',
                        'value'   => ucwords($patient_last_name),
                        'compare' => '='
                    )
                )
            ));
    
            if ($existing_patient) {
                $patient_id = $existing_patient[0]->ID;
            } else {
                $patient_post = array(
                    'post_title'  => $patient_title,
                    'post_type'   => 'patient',
                    'post_status' => 'publish',
                    'post_author' => $current_user_id,
                );
    
                $patient_id = wp_insert_post($patient_post);
    
                // Save the custom fields for the new patient
                update_post_meta($patient_id, 'wcpm_patient_first_name', ucwords($patient_first_name));
                update_post_meta($patient_id, 'wcpm_patient_last_name', ucwords($patient_last_name));
                update_post_meta($patient_id, 'wcpm_patient_dob', $patient_dob);
            }
        }
    
        // Save patient information to the order meta
        update_post_meta($order_id, '_selected_patient_id', $patient_id);
        update_post_meta($order_id, 'wcpm_patient_first_name', ucwords($patient_first_name));
        update_post_meta($order_id, 'wcpm_patient_last_name', ucwords($patient_last_name));
        update_post_meta($order_id, 'wcpm_patient_dob', $patient_dob);
        update_post_meta($order_id, 'wcpm_patient_dosage', $patient_dosage);
        update_post_meta($order_id, 'wcpm_patient_prescription_date', $patient_prescription_date);
    }    

    public function display_patient_information_on_thank_you_page($order_id) {
        $selected_patient_id = get_post_meta($order_id, '_selected_patient_id', true);
    
        if ($selected_patient_id) {
            $patient_first_name = get_post_meta($selected_patient_id, 'wcpm_patient_first_name', true);
            $patient_last_name = get_post_meta($selected_patient_id, 'wcpm_patient_last_name', true);
            $patient_dob = get_post_meta($selected_patient_id, 'wcpm_patient_dob', true);
        } else {
            $patient_first_name = get_post_meta($order_id, 'wcpm_patient_first_name', true);
            $patient_last_name = get_post_meta($order_id, 'wcpm_patient_last_name', true);
            $patient_dob = get_post_meta($order_id, 'wcpm_patient_dob', true);
        }

        $patient_dosage = get_post_meta($order_id, 'wcpm_patient_dosage', true);
        $patient_prescription_date = get_post_meta($order_id, 'wcpm_patient_prescription_date', true);
    
        $dob_formatted = !empty($patient_dob) ? date('d/m/Y', strtotime($patient_dob)) : '';
        $prescription_date_formatted = !empty($patient_prescription_date) ? date('d/m/Y', strtotime($patient_prescription_date)) : '';
    
        echo '<h2>' . __('Patient Information') . '</h2>';
        echo '<p><strong>' . __('First Name') . ':</strong> ' . esc_html(ucwords($patient_first_name)) . '</p>';
        echo '<p><strong>' . __('Last Name') . ':</strong> ' . esc_html(ucwords($patient_last_name)) . '</p>';
        echo '<p><strong>' . __('Date of Birth') . ':</strong> ' . esc_html($dob_formatted) . '</p>';
        echo '<p><strong>' . __('Prescription Dosage') . ':</strong> ' . esc_html($patient_dosage) . '</p>';
        echo '<p><strong>' . __('Prescription Date') . ':</strong> ' . esc_html($prescription_date_formatted) . '</p>';
    }    
    
    public function add_patient_info_to_order_email($order, $sent_to_admin, $plain_text) {
        $this->display_patient_information_on_thank_you_page($order->get_id());
    }    

    public function display_patient_fields_in_admin_order($order) {
        $this->display_patient_information_on_thank_you_page($order->get_id());
    }
}

new WC_PM_Checkout_Fields();

The first function adds extra fields which can either be populated by entering new information or by using a dropdown list that pulls information from a custom post type.

Once the order is submitted a new patient, if it doesn't already exist, is created and all these extra fields are added to the confirmation page, the email and the order meta. However, the order meta doesn't appear to be updating therefore it shows the field names but no values.

I created a test function that just runs on 'admin_init' and added my own order_id and this worked perfectly. There is something causing a conflict but can't seem to find the cause.

Plugins on the site:

I've checked code snippets but don't see anything in there that would cause problems. I haven't used Avada before.


Solution

  • First note that High-Performance Order Storage (HPOS) is now enabled by default in WooCommerce, and requires using some other hooks with CRUD objects getter and setter methods instead of WordPress Post meta functions.

    Also, be sure that your client doesn't use newly checkout blocks, that doesn't allow these kinds of customizations.

    First, try to replace the old hook woocommerce_checkout_update_order_meta from:

    add_action('woocommerce_checkout_update_order_meta', array($this, 'save_or_create_patient_on_checkout'), 999);
    

    with:

    add_action('woocommerce_checkout_create_order', array($this, 'save_or_create_patient_on_checkout'), 10, 1 );
    

    Then replace your related function with something like (the function argument is now $order, which is the WC_order object):

    public function save_or_create_patient_on_checkout( $order ) {
        $selected_patient_id = !empty($_POST['selected_patient_id']) ? intval($_POST['selected_patient_id']) : null;
        $patient_first_name = sanitize_text_field($_POST['wcpm_patient_first_name']);
        $patient_last_name = sanitize_text_field($_POST['wcpm_patient_last_name']);
        $patient_dob = !empty($_POST['wcpm_patient_dob']) ? date('Y-m-d', strtotime(str_replace('/', '-', $_POST['wcpm_patient_dob']))) : '';
        $patient_dosage = sanitize_text_field($_POST['wcpm_patient_dosage']);
        $patient_prescription_date = !empty($_POST['wcpm_patient_prescription_date']) ? date('Y-m-d', strtotime(str_replace('/', '-', $_POST['wcpm_patient_prescription_date']))) : '';
    
        if ($selected_patient_id) {
            // An existing patient was selected
            $patient_id = $selected_patient_id;
        } else {
            // Create a new patient post
            $patient_title = $patient_first_name . ' ' . $patient_last_name;
            $current_user_id = get_current_user_id();
    
            $existing_patient = get_posts(array(
                'post_type'  => 'patient',
                'author'     => $current_user_id,
                'post_status'=> 'publish',
                'meta_query' => array(
                    array(
                        'key'     => 'wcpm_patient_first_name',
                        'value'   => ucwords($patient_first_name),
                        'compare' => '='
                    ),
                    array(
                        'key'     => 'wcpm_patient_last_name',
                        'value'   => ucwords($patient_last_name),
                        'compare' => '='
                    )
                )
            ));
    
            if ($existing_patient) {
                $patient_id = $existing_patient[0]->ID;
            } else {
                $patient_post = array(
                    'post_title'  => $patient_title,
                    'post_type'   => 'patient',
                    'post_status' => 'publish',
                    'post_author' => $current_user_id,
                );
    
                $patient_id = wp_insert_post($patient_post);
    
                // Save the custom fields for the new patient
                update_post_meta($patient_id, 'wcpm_patient_first_name', ucwords($patient_first_name));
                update_post_meta($patient_id, 'wcpm_patient_last_name', ucwords($patient_last_name));
                update_post_meta($patient_id, 'wcpm_patient_dob', $patient_dob);
            }
        }
    
        // Save patient information as order metadata (compatible with HPOS)
        $order->update_meta_data('_selected_patient_id', $patient_id);
        $order->update_meta_data('wcpm_patient_first_name', ucwords($patient_first_name));
        $order->update_meta_data('wcpm_patient_last_name', ucwords($patient_last_name));
        $order->update_meta_data('wcpm_patient_dob', $patient_dob);
        $order->update_meta_data('wcpm_patient_dosage', $patient_dosage);
        $order->update_meta_data('wcpm_patient_prescription_date', $patient_prescription_date);
    }
    

    And to make the thankyou page display HPOS compatible, replace the related function with:

    public function display_patient_information_on_thank_you_page($order_id) {
        $order = wc_get_order($order_id); // Get the WC_Order object
    
        $selected_patient_id = $order->get_meta('_selected_patient_id'); // HPOS compatible
    
        if ($selected_patient_id) {
            $patient_first_name = get_post_meta($selected_patient_id, 'wcpm_patient_first_name', true);
            $patient_last_name = get_post_meta($selected_patient_id, 'wcpm_patient_last_name', true);
            $patient_dob = get_post_meta($selected_patient_id, 'wcpm_patient_dob', true);
        } else {
            $patient_first_name = $order->get_meta('wcpm_patient_first_name'); // HPOS compatible
            $patient_last_name = $order->get_meta('wcpm_patient_last_name'); // HPOS compatible
            $patient_dob = $order->get_meta('wcpm_patient_dob'); // HPOS compatible
        }
    
        $patient_dosage = $order->get_meta('wcpm_patient_dosage'); // HPOS compatible
        $patient_prescription_date = $order->get_meta('wcpm_patient_prescription_date'); // HPOS compatible
    
        $dob_formatted = !empty($patient_dob) ? date('d/m/Y', strtotime($patient_dob)) : '';
        $prescription_date_formatted = !empty($patient_prescription_date) ? date('d/m/Y', strtotime($patient_prescription_date)) : '';
    
        echo '<h2>' . __('Patient Information') . '</h2>';
        echo '<p><strong>' . __('First Name') . ':</strong> ' . esc_html(ucwords($patient_first_name)) . '</p>';
        echo '<p><strong>' . __('Last Name') . ':</strong> ' . esc_html(ucwords($patient_last_name)) . '</p>';
        echo '<p><strong>' . __('Date of Birth') . ':</strong> ' . esc_html($dob_formatted) . '</p>';
        echo '<p><strong>' . __('Prescription Dosage') . ':</strong> ' . esc_html($patient_dosage) . '</p>';
        echo '<p><strong>' . __('Prescription Date') . ':</strong> ' . esc_html($prescription_date_formatted) . '</p>';
    }
    

    Now your code is compatible with High-Performance Order Storage and should work with or without HPOS enabled.

    So to resume, for WooCommerce orders metadata:

    When updating order metadata the save() method is required, except with: