在 Woocommerce 3 中保存时发送带有附加帐户字段值的自定义电子邮件

Send a custom email with additional account fields values on save in Woocommerce 3

在插件“高级自定义字段”的基础上,为 WooCommerce 用户的个人帐户创建了额外的字段。使用第二个 "ACF for WooCommerce" 插件,我将这些字段放在编辑帐户页面上。

如果用户填写了字段或对其进行了编辑,管理员将收到一封包含这些字段值的电子邮件。以下代码负责通知:

if (!class_exists('WooCommerceNotifyChanges')) {

    class WooCommerceNotifyChanges {

        function __construct() {                        
            add_action('woocommerce_save_account_details', array($this, 'woocommerce_send_notification'), 15, 1);
        }

        function woocommerce_send_notification($user_id) {
            $body = '';
            $to = 'info@domain.com'; 
            $subject = 'Edit profile data';

            update_meta_cache('user', $user_id); // delete cache   

            $user = new WP_User($user_id);
            $user_name = $user->user_login;

            $body .= '<table>';
            $body .= '<tr><td>'.__("Profile"). '</td></tr>';                        
            $body .= '<tr><td>Login: </td><td>'.$user_name. '</td></tr>';
            $body .= '<tr><td>First Name: </td><td>'.$user->billing_first_name. '</td></tr>';
            $body .= '<tr><td>Last Name: </td><td>'.$user->billing_last_name. '</td></tr>';
            $body .= '<tr><td>Phone: </td><td>'.get_user_meta($user_id, 'field_5b4640119354c', $single=true). '</td></tr>'; //text field
            $body .= '<tr><td>Age: </td><td>'.get_user_meta($user_id, 'field_5b462d304b101', $single=true). '</td></tr>'; //text field
            $body .= '<tr><td>Family: </td><td>'.get_user_meta($user_id, 'field_5b4bd7d9f0031', $single=true). '</td></tr>'; // selector                       
            $body .= '<tr><td>What style do you prefer? </td><td>'.get_user_meta($user_id, 'field_5b47917a378ed', $single=true). '</td></tr>'; // checkbox                      

            $body .= '</table>';

            //set content type as HTML
            $headers = array('Content-Type: text/html; charset=UTF-8;');

            //send email
            if (wp_mail($to, $subject, $body, $headers)) {
                //echo 'email sent';
            } else {
                //echo 'email NOT sent';                
            }
            //exit();
        }
    }
    new WooCommerceNotifyChanges();
}

这里出现两个错误:

  1. 当用户编辑这些字段时,旧的、明显缓存的数据会发送到管理员电子邮件。当您在不编辑的情况下重新发送时,您将收到正确的字段数据。

    我放了一行代码:

    update_meta_cache('user', $user_id);
    

    但是不行,旧数据还在。显然,我做错了什么。

  2. 所有字段的数据都正确存储在数据库中,也正确显示在给管理员的电子邮件中。复选框的问题。

    在这种情况下,显示 "What style do you prefer?" 三个复选框 "Classic"、"Casual" 和 "Sport"。用户选择了 "Casual" 复选框。此字段的值已正确存储在数据库中。

    但是在给管理员的邮件中,没有这个值,只有“Array”这个词。

告诉我如何解决?

感谢任何帮助。

更新 1 - 解决插件问题。

1)缓存问题:我终于在“ACF for WooCommerce”插件源代码中找到了问题。

You need to use a higher priority value as the plugin uses a priority of 100

因此您将在代码中替换(在构造函数中):

add_action('woocommerce_save_account_details', array($this, 'woocommerce_send_notification'), 15, 1);

按优先级值 150 (或更多) 例如:

add_action('woocommerce_save_account_details', array($this, 'woocommerce_send_notification'), 150, 1);

它应该最终解决你的假“缓存”问题(感谢 Kashalo)


2)“您喜欢哪种风格?”的复选框字段

您需要将数组转换为以逗号分隔值的字符串…

使用以下内容:

function woocommerce_send_notification($user_id) {
    // Recipient
    $to = 'info@domain.com'; 
    
    $subject = 'Edit profile data';

    $user =      new WP_User($user_id);
    $user_name = $user->user_login;
    
    $phone  = get_user_meta( $user_id, 'field_5b4640119354c', true ); // Phone - text field
    $age    = get_user_meta( $user_id, 'field_5b462d304b101', true ); // Age- text field
    $family = get_user_meta( $user_id, 'field_5b4bd7d9f0031', true ); // Family - select field 
    $fstyle = get_user_meta( $user_id, 'field_5b47917a378ed', true ); // Favorited styles - Multiple Checkboxes 

     // Convert the array to a coma separated list (Favorited styles)
    $fstyle = implode( ', ',  $fstyle ); 
    
    // Body output
    $body = '<h3>'. __("Profile") .'</h3>
    <table>
    <tr><td>Login: </td><td>'.      $user->user_login         .'</td></tr>
    <tr><td>First Name: </td><td>'. $user->billing_first_name .'</td></tr>
    <tr><td>Last Name: </td><td>'.  $user->billing_last_name  .'</td></tr>
    <tr><td>Phone: </td><td>'.      $phone                    .'</td></tr>
    <tr><td>Age: </td><td>'.        $age                      .'</td></tr>
    <tr><td>Family: </td><td>'.     $family                   .'</td></tr>  
    <tr><td>What style do you prefer? </td><td>'. $fstyle     .'</td></tr> 
    </table>';

    //set content type as HTML
    $headers = array('Content-Type: text/html; charset=UTF-8;');

    //send email
    $sent = wp_mail( $to, $subject, $body, $headers );
}

原回答:

Testing and trying to reproduce your issue is not possible within the provided information and code in this question, as nobody can guess your ACF settings and related context.

Using Advanced Custom fields is not a good idea for that, when doing multiple customizations. You are loosing so much time, trying to find out what is wrong since a while.

The best way is to hand code custom fields without using a plugin as developers do.

在下面的代码中我是:

  1. 在 CLASS 本身 中添加和显示附加字段而不使用 ACF,
  2. 正在验证字段数据,
  3. 保存数据并发送包含正确数据的电子邮件通知(没有任何缓存问题),
  4. 在 woocommerce 计费部分下的管理员用户编辑页面中显示这些附加字段。

Now as you dont give the option values for the "Family" select field and for the "Preferred style" checkboxes, you will have to set the desired values in the settings section in each array.

When testing this code you will have to disable the related ACF fields.

完整功能代码(无ACF):

if ( ! class_exists('WC_Additional_Account_fields') ) {

    class WC_Additional_Account_fields {
        // 1. Constructor
        function __construct() {

            ## A. Settings

            // The text domain
            $this->text_domain    = 'aafields';

            // The current user WP_User object
            $this->user           = wp_get_current_user();

            // The Admin email
            $this->admin_email    = get_option('admin_email');

            // The array of options for "Family" and "preferred style"
            $this->fields_options = array(
                // FAMILY available options array
                'family' => array(
                    __("No childs", $this->text_domain ),
                    __("One child", $this->text_domain ),
                    __("2 childs", $this->text_domain ),
                    __("3 or more childs", $this->text_domain ),
                ),
                // PREFERRED STYLE available options array
                'preferred_styles' => array(
                    __("Style 1", $this->text_domain ),
                    __("Style 2", $this->text_domain ),
                    __("Style 3", $this->text_domain ),
                    __("Style 4", $this->text_domain ),
                ),
            );

            ## B. Hooking functions

            add_action('woocommerce_edit_account_form', array($this, 'add_other_fields_in_edit_account_form') );
            add_action('woocommerce_save_account_details', array($this, 'save_other_fields_in_edit_account_form'), 15, 1);
            add_action('woocommerce_save_account_details_errors', array($this, 'validate_other_fields_in_edit_account_form'), 15, 1 );
            add_filter('woocommerce_customer_meta_fields', array($this, 'add_customer_meta_fields_to_admin_user_list'), 15, 1 );
        }


        // 1. Displaying additional account fields (+ jQuery script to handle checkboxes as radio buttons)
        public function add_other_fields_in_edit_account_form() {
            $text_domain = $this->text_domain;
            $user        = $this->user;
            $options     = $this->fields_options;

            $phone  = isset($user->phone) ? $user->phone : '';
            $age    = isset($user->billing_age) ? $user->billing_age : '';
            $family = isset($user->billing_family) ? $user->billing_family : '';
            $pstyle = isset($user->billing_pref_style) ? $user->billing_pref_style : '';

            // Inline styles
            ?>
            <style>
                #billing_pref_style_field span.label {padding:0 12px 0 6px;}
                div.clear {clear:both;margin-bottom:1.4em;}
            </style>
            <?php

            // Fields 1 and 2
            ?>
            <p class="woocommerce-form-row woocommerce-form-row--first form-row form-row-first">
                <label for="phone"><?php _e( "Phone number", $text_domain ); ?><span class="required">*</span></label>
                <input type="text" class="woocommerce-Input woocommerce-Input--text input-text" name="phone" id="phone" value="<?php echo $phone; ?>" />
            </p>
            <p class="woocommerce-form-row woocommerce-form-row--last form-row form-row-last">
                <label for="billing_age"><?php _e( "Age", $text_domain ); ?><span class="required">*</span></label>
                <input type="text" class="woocommerce-Input woocommerce-Input--text input-text" name="billing_age" id="billing_age" value="<?php echo $age; ?>" />
            </p>
            <?php

            // Fields 3 and 4 (Select field + Grouped checkboxes)
            ?>
            <p class="woocommerce-form-row woocommerce-form-row--last form-row form-row-first">
                <label for="billing_family"><?php _e( "Family", $text_domain ); ?><span class="required">*</span></label>
                <select id="billing_family" class="woocommerce-Select woocommerce-Select--option select" name="billing_family">
                    <option value=""><?php _e( "Select a value", $text_domain ); ?></option>
                    <?php
                        foreach( $options['family'] as $option ) :
                            $selected = ( $family == $option ) ? ' selected="selected"' : '';
                            echo '<option value="' . $option . '"' . $selected . '>' . $option . '</option>';
                        endforeach;
                    ?>
                </select>
            </p>
            <p id="billing_pref_style_field" class="woocommerce-form-row woocommerce-form-row--last form-row form-row-last">
                <label for="preferred_styles"><?php _e( "What style do you prefer? ", $text_domain ); ?><span class="required">*</span></label>
                    <?php
                        foreach( $options['preferred_styles'] as $key => $option ) :
                            $checked = $pstyle == $option ? ' checked="checked"' : '';
                            echo '<span><input type="checkbox" class="woocommerce-Input woocommerce-Input--checkbox input-checkbox" name="pref_style" value="' . $option . '"' . $checked . '><span class="label">' . $option . '</span></span>';
                        endforeach;
                    ?>
                </select>
                <?php $value = ! empty($pstyle) ? $pstyle : $options['preferred_styles'][0];
                // Hidden field that catch the active checkbox value ?>
                <input type="hidden"  name="billing_pref_style" value="<?php echo $value; ?>">
            </p>
            <div class="clear"></div>
            <?php

            // jQuery code: Enabling grouped checkboxes and passing the chosen value to the hidden field
            ?>
            <script type="text/javascript">
            jQuery(function($){
                var a = '#billing_pref_style_field',        b = a+' input[type="checkbox"]',
                    c = a+' input[type="hidden"]';

                $(b+'[value="'+$(c).val()+'"]').attr('checked', true);
                $(b).click( function(){
                    var d = $(this).val();
                    $(c).val(d);
                    $(b).each( function( i, v ) {
                        if( d != $(v).val() ){
                            $(v).prop('checked', false);
                        } else {
                            $(v).prop('checked', true);
                        }
                    });
                });
            });
            </script>
            <?php
        }

        // 2. Additional account fields validation
        public function validate_other_fields_in_edit_account_form( $args ){
            if ( isset($_POST['phone']) && empty($_POST['phone']) )
                $args->add( 'error', __( 'Please fill in the "Phone" field', 'woocommerce' ),'');

            if ( isset($_POST['billing_age']) && empty($_POST['billing_age']) )
                $args->add( 'error', __( 'Please fill in the "Age" field', 'woocommerce' ),'');

            if ( isset($_POST['billing_family']) && empty($_POST['billing_family']) )
                $args->add( 'error', __( 'Please choose a value for the "Family" field', 'woocommerce' ),'');

            if ( isset($_POST['billing_pref_style']) && empty($_POST['billing_pref_style']) )
                $args->add( 'error', __( 'Please choose a "Preferred style"', 'woocommerce' ),'');
        }

        // 3. Save custom additional fields value + Send custom email
        public function save_other_fields_in_edit_account_form( $user_id ) {
            $text_domain = $this->text_domain;
            $send_notification = false;

            if( isset( $_POST['phone'] ) ){
                update_user_meta( $user_id, 'phone', sanitize_text_field( $_POST['phone'] ) );
                update_user_meta( $user_id, 'billing_phone', sanitize_text_field( $_POST['phone'] ) );
                if( ! empty( $_POST['phone'] ) )
                    $send_notification = true;
            }

            // For Favorite color 2
            if( isset( $_POST['billing_age'] ) ){
                update_user_meta( $user_id, 'billing_age', sanitize_text_field( $_POST['billing_age'] ) );
                if( ! empty( $_POST['billing_age'] ) )
                    $send_notification = true;
            }

            // For Billing email (added related to your comment)
            if( isset( $_POST['billing_family'] ) ){
                update_user_meta( $user_id, 'billing_family', esc_attr( $_POST['billing_family'] ) );
                if( ! empty( $_POST['billing_family'] ) )
                    $send_notification = true;
            }

            // For Billing email (added related to your comment)
            if( isset( $_POST['billing_pref_style'] ) ){
                update_user_meta( $user_id, 'billing_pref_style', esc_attr( $_POST['billing_pref_style'] ) );
                if( ! empty( $_POST['billing_pref_style'] ) )
                    $send_notification = true;
            }

            if( $send_notification ){
                $user = new WP_User($user_id);

                $to = $this->admin_email;
                $subject = __('Edited profile data for: ') . $user->billing_first_name . ' ' . $user->billing_last_name;

                $body = '<h3>' . __("Profile", $text_domain ) . '</h3>
                <table>
                <tr><th align="left">' . __("Login:", $text_domain ) . '</th><td>'. $user->user_login . '</td></tr>
                <tr><th align="left">' . __("First Name:", $text_domain ) . ' </th><td>' . $user->billing_first_name . '</td></tr>
                <tr><th align="left">' . __("Last Name:", $text_domain ) . ' </th><td>' . $user->billing_last_name . '</td></tr>
                <tr><th align="left">' . __("Phone:", $text_domain ) . ' </th><td>' . $user->phone . '</td></tr>
                <tr><th align="left">' . __("Age:", $text_domain ) . ' </th><td>' . $user->billing_age . '</td></tr>
                <tr><th align="left">' . __("Family:", $text_domain ) . ' </th><td>' . $user->billing_family . '</td></tr>
                <tr><th align="left">' . __("Preferred style", $text_domain ) . ' </th><td>' . $user->billing_pref_style . '</td></tr>
                </table>';

                //set content type as HTML
                $headers = array('Content-Type: text/html; charset=UTF-8;');

                //send email
                wp_mail($to, $subject, $body, $headers);
            }
        }

        // 4. Add the additional fields in admin user list in the billing section
        public function add_customer_meta_fields_to_admin_user_list( $args ) {
            $domain  = $this->text_domain;
            $options = $this->fields_options;

            // Age
            $args['billing']['fields']['billing_age'] = array(
                'label'       => __( "Age", $text_domain ),
                'description' => '',
            );

            // Family
            $args['billing']['fields']['billing_family'] = array(
                'label'       => __( 'Family', 'woocommerce' ),
                'description' => '',
                'type'        => 'select',
                'options'     => array( '' => __( 'Select a value', $domain ) ) + array_combine($options['family'], $options['family']),
            );

            // Preferred styles
            $args['billing']['fields']['billing_pref_style'] = array(
                'label'       => __( 'Preferred styles', 'woocommerce' ),
                'description' => '',
                'type'        => 'select',
                'options'     => array( '' => __( 'Select a value', $domain ) ) + array_combine($options['preferred_styles'], $options['prefered_styles']),
            );

            return $args;
        }
    }
    new WC_Additional_Account_fields();
}

代码进入您的活动子主题(活动主题)的 function.php 文件。已测试并有效。

The phone field updates both user phone and user billing phone at the same time.


我的帐户 > 帐户详细信息部分的附加字段:


字段验证:


电子邮件通知(关于“保存更改”)充满了新保存的数据:


最后,在帐单部分下的管理员用户页面中显示的字段: