Woocommerce新增头像表单:绕过save_account_details必填项校验
问题描述
在WooCommerce的form-edit-account.php模板中新增了用户头像更换表单,原本计划通过update_user_meta更新用户image元键,但点击上传按钮时,绑定的woocommerce_save_account_details钩子会因检测到原账户表单的必填字段(姓名、邮箱等)未填写,导致保存逻辑无法执行。需要在不关闭必填项校验的前提下,找到其他实现头像字段保存的方案。
原新增表单代码:
<!-- START FORM CHANGE SETTINGS --> <form name="Form-Avatar" class="edit-avatar" action="" method="post" enctype="multipart/form-data" <?php do_action( 'woocommerce_edit_account_form_tag' );?>> <!-- Avatar Section --> <div class="global_container avatar"> <div class="avatar_col_1"><?php // Visualizza Avatar if ($attachment_id) { $original_image_url = wp_get_attachment_url($attachment_id); // Display Image instead of URL echo wp_get_attachment_image($attachment_id, $size = array('150', '150')); // Invece dell'array size, stava 'full' come parametro. } else { ?><img style="" src="<?php echo get_avatar_url($user_id, array ('size' => 150)); ?>" alt=""><?php } ?></div> <div class="avatar_col_2"> <div class="upload-field"> <p class="form-row avatar"> <div class="field-settings upload"> <label for="file" class="chose_file_button">Scegli immagine</label> <label id="file-name" for="file" class="chose_file_input">Nessuna immagine selezionata</label> </div> <input id="file" type="file" style="display:none;" class="field-settings avatar" name="image" accept="image/x-png,image/gif,image/jpeg"> <span class="t4-light avatar"><?php esc_html_e( "File consentiti: png/gif/jpeg, dimensione massima 1MB. L'immagine del tuo profilo verrà visualizzata nelle recensioni dei prodotti e nei commenti.", 'woocommerce' ); ?></span> </p> </div> <!-- Show file name inside label avatar --> <script> $("#file").change(function(){ $("#file-name").text(this.files[0].name); }); </script> <div class="avatar_button"> <!-- Upload Avatar --> <p style="margin-bottom: 0px!important;"> <?php wp_nonce_field( 'save_account_details', 'save-account-details-nonce-avatar' ); ?> <button type="submit" class="edit-account-button" name="save_account_details" value="<?php esc_attr_e( 'Save changes', 'woocommerce' ); ?>"><?php esc_html_e( 'Upload Avatar', 'woocommerce' ); ?></button> <input type="hidden" name="action" value="save_account_details" /> </p> <?php // Rimuovi immagine Button if (isset($_GET['rm_profile_image_id'])) { if ($attachment_id == $_GET['rm_profile_image_id']) { wp_delete_attachment($attachment_id); if (delete_user_meta($user_id, 'image')) { wp_delete_attachment($attachment_id); } ?><script>window.location='<?php echo wc_get_account_endpoint_url('impostazioni') ?>';</script><?php exit(); } } else { echo '<a href=' . wc_get_account_endpoint_url('impostazioni') . '?rm_profile_image_id=' . $attachment_id . '> ' . __('Remove') . ' </a>'; } ?> </div> </div> </div> </form>
原保存函数代码:
// Save Avatar Action function action_woocommerce_save_account_details( $user_id ) { if ( isset( $_FILES['image'] ) ) { require_once( ABSPATH . 'wp-admin/includes/image.php' ); require_once( ABSPATH . 'wp-admin/includes/file.php' ); require_once( ABSPATH . 'wp-admin/includes/media.php' ); // Imposta la cartella in cui salvare gli avatar degli utenti function wp_set_custom_upload_folder($uploads) { $uploads['path'] = $uploads['basedir'] . '/avatar-upload'; $uploads['url'] = $uploads['baseurl'] . '/avatar-upload'; if (!file_exists($uploads['path'])) { mkdir($uploads['path'], 0755, true); } return $uploads; } add_filter('upload_dir', 'wp_set_custom_upload_folder'); $attachment_id = media_handle_upload( 'image', 0 ); if ( is_wp_error( $attachment_id ) ) { update_user_meta( $user_id, 'image', $_FILES['image']['error'] . ": " . $attachment_id->get_error_message() ); } else { $old_attachment_id = get_user_meta( $user_id, 'image', true ); wp_delete_attachment($old_attachment_id); update_user_meta( $user_id, 'image', $attachment_id ); } remove_filter('upload_dir', 'wp_set_custom_upload_folder'); } } add_action( 'woocommerce_save_account_details', 'action_woocommerce_save_account_details', 10, 1 );
解决方案
通过自定义表单提交action和专属钩子,让头像上传逻辑独立于原账户表单的校验流程,具体步骤如下:
1. 修改头像表单的提交配置
将表单的隐藏action字段改为自定义值(比如save_user_avatar),同时更新nonce验证的标识,避免触发原账户保存的校验逻辑:
<!-- START FORM CHANGE SETTINGS --> <form name="Form-Avatar" class="edit-avatar" action="" method="post" enctype="multipart/form-data"> <!-- Avatar Section --> <div class="global_container avatar"> <div class="avatar_col_1"><?php // 初始化用户ID和头像附件ID $user_id = get_current_user_id(); $attachment_id = get_user_meta($user_id, 'image', true); // 显示头像 if ($attachment_id) { echo wp_get_attachment_image($attachment_id, array('150', '150')); } else { ?><img style="" src="<?php echo get_avatar_url($user_id, array ('size' => 150)); ?>" alt=""><?php } ?></div> <div class="avatar_col_2"> <div class="upload-field"> <p class="form-row avatar"> <div class="field-settings upload"> <label for="file" class="chose_file_button">Scegli immagine</label> <label id="file-name" for="file" class="chose_file_input">Nessuna immagine selezionata</label> </div> <input id="file" type="file" style="display:none;" class="field-settings avatar" name="image" accept="image/x-png,image/gif,image/jpeg"> <span class="t4-light avatar"><?php esc_html_e( "File consentiti: png/gif/jpeg, dimensione massima 1MB. L'immagine del tuo profilo verrà visualizzata nelle recensioni dei prodotti e nei commenti.", 'woocommerce' ); ?></span> </p> </div> <!-- 显示选中的文件名 --> <script> $("#file").change(function(){ $("#file-name").text(this.files[0].name); }); </script> <div class="avatar_button"> <!-- 上传按钮区域 --> <p style="margin-bottom: 0px!important;"> <?php wp_nonce_field( 'save_user_avatar_action', 'save_user_avatar_nonce' ); ?> <button type="submit" class="edit-account-button" name="save_user_avatar" value="<?php esc_attr_e( 'Upload Avatar', 'woocommerce' ); ?>"><?php esc_html_e( 'Upload Avatar', 'woocommerce' ); ?></button> <input type="hidden" name="action" value="save_user_avatar" /> </p> <!-- 删除头像按钮 --> <?php if (isset($_GET['rm_profile_image_id'])) { if ($attachment_id == $_GET['rm_profile_image_id']) { wp_delete_attachment($attachment_id); delete_user_meta($user_id, 'image'); ?><script>window.location='<?php echo wc_get_account_endpoint_url('impostazioni') ?>';</script><?php exit(); } } else { if ($attachment_id) { echo '<a href=' . wc_get_account_endpoint_url('impostazioni') . '?rm_profile_image_id=' . $attachment_id . '> ' . __('Remove') . ' </a>'; } } ?> </div> </div> </div> </form>
2. 编写独立的头像保存函数并绑定自定义钩子
使用WordPress的admin_post_{action}和admin_post_nopriv_{action}钩子,专门处理头像上传请求,完全绕过原账户表单的必填项校验:
// 保存用户头像逻辑 function save_user_avatar_action() { // 验证用户登录状态和nonce合法性 if (!is_user_logged_in() || !isset($_POST['save_user_avatar_nonce']) || !wp_verify_nonce($_POST['save_user_avatar_nonce'], 'save_user_avatar_action')) { wp_redirect(wc_get_account_endpoint_url('impostazioni')); exit; } $user_id = get_current_user_id(); // 检测文件上传是否正常 if (isset($_FILES['image']) && $_FILES['image']['error'] == UPLOAD_ERR_OK) { require_once(ABSPATH . 'wp-admin/includes/image.php'); require_once(ABSPATH . 'wp-admin/includes/file.php'); require_once(ABSPATH . 'wp-admin/includes/media.php'); // 设置自定义头像上传目录 add_filter('upload_dir', function($uploads) { $uploads['path'] = $uploads['basedir'] . '/avatar-upload'; $uploads['url'] = $uploads['baseurl'] . '/avatar-upload'; if (!file_exists($uploads['path'])) { mkdir($uploads['path'], 0755, true); } return $uploads; }); // 处理文件上传 $attachment_id = media_handle_upload('image', 0); if (is_wp_error($attachment_id)) { // 上传失败时记录错误信息 update_user_meta($user_id, 'image', $_FILES['image']['error'] . ": " . $attachment_id->get_error_message()); } else { // 删除旧头像 $old_attachment_id = get_user_meta($user_id, 'image', true); if ($old_attachment_id && is_numeric($old_attachment_id)) { wp_delete_attachment($old_attachment_id, true); } // 保存新头像ID到用户元数据 update_user_meta($user_id, 'image', $attachment_id); } remove_filter('upload_dir', '__return_false'); } // 跳转回账户设置页面 wp_redirect(wc_get_account_endpoint_url('impostazioni')); exit; } // 登录用户触发的钩子 add_action('admin_post_save_user_avatar', 'save_user_avatar_action'); // 非登录用户兼容钩子(此处场景下可保留) add_action('admin_post_nopriv_save_user_avatar', 'save_user_avatar_action');
方案说明
- 该方案让头像上传表单独立提交,完全不触发原账户表单的必填项校验流程,同时保留了原表单的校验规则。
- 新增了用户登录状态和nonce验证,提升了操作安全性。
- 优化了旧头像删除逻辑,避免重复删除和无效操作。
内容的提问来源于stack exchange,提问作者Snorlax
相关产品推荐
相关产品推荐

