Отправьте собственное электронное письмо с дополнительными значениями полей учетной записи при сохранении в Woocommerce 3

На базе плагина «Advanced Custom Fields» созданы дополнительные поля для личного кабинета пользователей 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 = '[email protected]'; 
            $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. Данные всех полей корректно хранятся в базе данных, а также корректно отображаются в письме администратору. Проблема с чекбоксами.

    В данном случае вопрос "Какой стиль вам больше нравится?" отображаются три флажка «Классический», «Повседневный» и «Спорт». Пользователь установил флажок «Повседневный». Значение этого поля корректно сохраняется в базе данных.

    Но в письме администратору вместо этого значения приходит только слово «Массив».

Подскажите как исправить?

Любая помощь приветствуется.


person Dmitry    schedule 04.10.2018    source источник
comment
Никто не может помочь? ((   -  person Dmitry    schedule 05.10.2018
comment
Я не знаю woocommerce, но, возможно, это может помочь. Вы уверены, что woocommerce_send_notification() выполняется ПОСЛЕ сохранения пользовательских данных? Я предполагаю, что не по вашему рассказу. Как выглядит массив, содержащий «стиль»? Может быть, это поможет распечатать его в виде массива по почте: print_r(get_user_meta($user_id, 'field_5b47917a378ed', $single=true), true)   -  person Piemol    schedule 06.10.2018


Ответы (1)


Обновление 1 - Решение проблем с плагином.

1) Проблема с кэшированием: я наконец нашел проблему в исходном коде плагина ACF для WooCommerce.

Вам нужно использовать более высокое значение приоритета, так как подключаемый модуль использует приоритет 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 = '[email protected]'; 
    
    $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 );
}

Оригинальный ответ:

Тестирование и попытка воспроизвести вашу проблему невозможно в рамках предоставленной информации и кода в этом вопросе, поскольку никто не может угадать ваши настройки ACF и соответствующий контекст.

Использование расширенных настраиваемых полей не является хорошей идеей при выполнении нескольких настроек. Вы теряете так много времени, пытаясь выяснить, что не так с тех пор.

Лучший способ — добавить настраиваемые поля кода без использования подключаемого модуля, как это делают разработчики.

В следующем коде я:

  1. Добавление и отображение дополнительных полей в самом КЛАССЕ без ACF,
  2. Проверка данных полей,
  3. Сохранение данных и отправка уведомления по электронной почте с правильными данными (без проблем с кэшем),
  4. отображение этих дополнительных полей на страницах редактирования пользователя администратора в разделе выставления счетов woocommerce.

Теперь, поскольку вы не указываете значения параметров для поля выбора Family и для флажков Предпочитаемый стиль, вам придется установить нужные значения в разделе настроек в каждом массиве.

При тестировании этого кода вам придется отключить соответствующие поля ACF.

Полный функциональный код (без 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 вашей активной дочерней темы (активной темы). Проверено и работает.

Поле телефона обновляет как телефон пользователя, так и телефон пользователя для выставления счетов одновременно.


Дополнительные поля в разделе Моя учетная запись › Сведения об учетной записи:

введите здесь описание изображения


Проверка полей:

введите здесь описание изображения


Уведомление по электронной почте (при сохранении изменений) со свежими сохраненными данными:

введите здесь описание изображения


И, наконец, отображаемые поля на страницах пользователей-администраторов в разделе оплаты:

введите здесь описание изображения

person LoicTheAztec    schedule 07.10.2018
comment
Спасибо за такой подробный ответ! Как я понимаю, чтобы протестировать этот код, мне нужно полностью деактивировать плагин ACF? Если я хочу создать новые настраиваемые поля, нужно ли мне делать их похожими на ваши? - person Dmitry; 07.10.2018
comment
Может быть проблема в том, что эти поля будут отображаться при регистрации пользователя. Поэтому я использовал плагин «ACF for Woocommerce» и поместил эти поля в «Edit Account Form Start», тогда при регистрации они не показывались. - person Dmitry; 07.10.2018
comment
Когда я помещал ваш код в файл functions.php, у меня были дублированные дополнительные поля после электронной почты и после смены пароля. - person Dmitry; 07.10.2018
comment
Это что-то сложное, и у меня есть работа… Итак, я начал, но еще не закончил… Наберитесь терпения. - person LoicTheAztec; 09.10.2018
comment
Ok. Я буду ждать. Спасибо! - person Dmitry; 09.10.2018
comment
@Dmitry Наконец-то я нашел проблему в исходном коде плагина… Итак, у меня есть 2 ответа на ваши реальные проблемы с кодом… - person LoicTheAztec; 10.10.2018
comment
Большое спасибо за ответ! Все работает! - person Dmitry; 10.10.2018