You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 13:36:31