B1: Đăng ký thư viện trong admin WordPress

function tmdev_add_style_scripts() {

  $js_list_an_item_ver = date("ymd-Gis", filemtime( get_template_directory(). '/assets/js/list-an-item.js' )); 

  $assets_path = get_template_directory_uri();
  wp_enqueue_script('jquery-validation', $assets_path.'/assets/js/jquery.validate.min.js', array('jquery'));	
  wp_enqueue_script('product-submit', $assets_path.'/assets/js/list-an-item.js', array('jquery', 'custom-script'), $js_list_an_item_ver );		
  wp_enqueue_style( 'product-submit', $assets_path.'/assets/css/product-submit.css', '', 123 );	
}
add_action( 'wp_enqueue_scripts', 'tmdev_add_style_scripts' );

B2: Tạo form và xử lý validate ở ngoài Frontend

<div id="product-submission-form">
    <div class="step-buttons">
        <button type="button" class="step-btn" data-step="1">STEP ONE</button>
        <button type="button" class="step-btn" data-step="2">STEP TWO</button>
        <button type="button" class="step-btn" data-step="3">STEP THREE</button>
    </div>        
    <form id="productForm">
        <div class="step step-1">
            <div class="step-name"><p>STEP ONE:<br>Product Information</p></div>
            <div class="form-group">
                <label for="productName">Product Name</label>
                <input type="text" id="productName" name="productName" required>
            </div>
            <div class="form-group">
                <label for="productDescription">Product Description</label>
                <textarea id="productDescription" name="productDescription" required></textarea>
            </div>
            <button type="button" class="next-step">Next</button>
        </div>
        <div class="step step-2" style="display: none;">
            <div class="step-name"><p>STEP TWO:<br>Pricing Information*</p></div>
            <div class="form-group">
                <label for="productPrice">Price</label>
                <input type="number" id="productPrice" name="productPrice" required>
            </div>
            <div class="form-group">
                <label for="productImage">Product Image</label>
                <input type="file" id="productImage" name="productImage" required>
            </div>
            <button type="button" class="previous-step">Previous</button>
            <button type="submit">Submit</button>                
        </div>
        <div class="step step-3" style="display: none;">
            <div class="step-name"><p>STEP THREE:<br>Preview & List*</p></div>
            Layout
        </div>
    </form>
</div>
jQuery(document).ready(function($) {
    var currentStep = 1;
    var $form = $("#productForm");

    $form.validate({
        errorClass: "invalid",
        errorElement: "div",
        rules: {
            productName: "required",
            productDescription: "required",
            productPrice: {
                required: true,
                number: true
            },
            productImage: "required"
        },
        messages: {
            productName: "Please enter the product name",
            productDescription: "Please enter the product description",
            productPrice: "Please enter a valid price",
            productImage: "Please select an image"
        }
    });

    function showStep(step) {
        $(".step").hide();
        $(".step-" + step).show();
        $(".step-btn").removeClass("active");
        $('.step-btn[data-step="' + step + '"]').addClass("active");
    }

    $(".next-step").click(function() {
        if ($form.valid()) {
            currentStep++;
            showStep(currentStep);
        }
    });

    $(".previous-step").click(function() {
        currentStep--;
        showStep(currentStep);
    });

    $(".step-btn").click(function() {
        var step = $(this).data("step");
        if ($form.valid()) {
            currentStep = step;
            showStep(step);
        }
    });

    $form.on("submit", function(event) {
        event.preventDefault();
        if ($form.valid()) {
            var formData = new FormData($form[0]); // Create a FormData object

            $.ajax({
                url: product_submission_params.ajaxurl,
                type: 'POST',
                data: formData,
                processData: false,
                contentType: false,
                success: function(response) {
                    if (response.success) {
                        alert(response.data.message);
                    } else {
                        alert('An error occurred. Please try again.');
                    }
                }
            });
        }
    });

    showStep(currentStep); // Initialize form to show first step
    
});   

B3: Xử lý form và lưu vào quản trị WordPress

add_action('wp_ajax_product_submission', 'nt_handle_product_submission');
function nt_handle_product_submission() {
  
  // phpcs:disable WordPress.Security.NonceVerification.Missing
  if ( !isset( $_POST['nonce'] ) || !isset( $_POST['list_an_item'] ) ) {
    wp_send_json_error( 'missing_fields' );
    wp_die();
  }	

  // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
  if( !wp_verify_nonce( wp_unslash( $_POST['nonce'] ), 'file_upload' ) ){ 
    wp_send_json_error( 'bad_nonce' );
    wp_die();
  }

  // phpcs:disable WordPress.Security.NonceVerification.Missing
  if( $_POST['list_an_item'] != '1' ){
    wp_send_json_error( 'bad_nonce' );
    wp_die();
  }

  $current_user 		= wp_get_current_user();
  $user_id	 		= $current_user->ID;
  $user_email 		= $current_user->user_email;
  $user_display_name 	= $current_user->display_name;

    // Validate and sanitize inputs
    $productName 		= sanitize_text_field($_POST['productName']);
    $productCategory 	= intval($_POST['productCategory']);
    $productColour 		= intval($_POST['productColour']);
    $productBrand 		= $_POST['productBrand'] != 'add-new-brand' ? intval($_POST['productBrand']) : $_POST['productBrand'];
    $productSize 		= intval($_POST['productSize']);

  if( $productBrand == 'add-new-brand' ){
    $newBrand = sanitize_text_field($_POST['newBrand']);

    $term_check = term_exists( $newBrand, 'brand' ); // array is returned if taxonomy is given
    if(  $term_check !== 0 && $term_check !== null ){
      $productBrand = (int)$term_check['term_id']; 
    }else{
      $productBrandObj = wp_insert_term( $newBrand, 'brand' );
      $productBrand = (int)$productBrandObj['term_id'];
    }
  }

  $componentsRentalPrice = [
    'dailyRate' 			=> intval($_POST['dailyRate']),
    'dryCleaning' 		=> intval($_POST['dryCleaning']),
    'delivery' 			=> intval($_POST['delivery']),
    'securityDeposit' 	=> intval($_POST['securityDeposit'])
  ];

    $productDetails = sanitize_textarea_field($_POST['productDetails']);
  $productSizeFit = sanitize_textarea_field($_POST['productSizeFit']);
  $productStyleNotes = sanitize_textarea_field($_POST['productStyleNotes']);

    $retailPrice = floatval($_POST['retailPrice']);
    $productPrice = floatval($_POST['productPrice']);

  $rentalOnly = true;
  if( isset( $_POST['sell-your-item'] ) ){
    $rentalOnly = false;
  }

  // Create the WooCommerce product
  $product = new WC_Product_Simple();
  $product->set_name($productName);
  $product->set_description($productDetails);
  // $product->set_sku( $sku );
  $product->set_regular_price($retailPrice);
  if( $productPrice != 0 ){
    $product->set_sale_price($productPrice);
  }	

  // Set product category
  $product->set_category_ids(array($productCategory));

  // Update product meta: product details, style note, size and fit
  $product->update_meta_data('product_details', $productDetails );
  $product->update_meta_data('style_notes', $productSizeFit );
  $product->update_meta_data('size_and_fit', $productStyleNotes );
  $product->update_meta_data('list_an_item', 1 );
  $product->update_meta_data('rental_price_components', $componentsRentalPrice );

  // Update rental products pricing
  $rentalProductsPrice = sanitize_text_field( $_POST['rentalProductsPrice'] );

  // Use a regular expression to get the values before the | sign
  $price_first = 0;
  $rentalPrice4Day = $rentalPrice8Day = $rentalPrice16Day = $rentalPrice30Day = 0;
  if( preg_match_all('/[^|]+(?=\|)/', $rentalProductsPrice, $matches) ){
    
    $argsRentalProductsPrice = explode("|",$rentalProductsPrice);		
    foreach( $argsRentalProductsPrice as $key => $item ){
      $price_item = explode( ':', $item );
      $day = (int)$price_item[0];
      switch ($day) {
        case 4:
          $rentalPrice4Day = $price_item[1];
          break;
        case 8:
          $rentalPrice8Day = $price_item[1];
          break;
        case 16:
          $rentalPrice16Day = $price_item[1];
          break;			
        case 30:
          $rentalPrice30Day = $price_item[1];
          break;			
        default:
          break;
      }
    }

    $product->update_meta_data('_wcrp_rental_products_pricing_period_additional_selections', $rentalProductsPrice );
    
    // allow purchase product
    if( !$rentalOnly ){
      $product->update_meta_data('_wcrp_rental_products_rental', 'yes_purchase' );
    }else{ // Rental only 
      $product->update_meta_data('_wcrp_rental_products_rental', 'yes' );
    }
    $product->update_meta_data('_wcrp_rental_products_pricing_type', 'period_selection' );
    $product->update_meta_data('_wcrp_rental_products_pricing_period', 4 );
    $product->update_meta_data('_wcrp_rental_products_rental_purchase_price', $rentalPrice4Day );
    $product->update_meta_data('_wcrp_rental_products_rental_stock', 1 );		
  }

  // Save product
  $product_id = $product->save();

    if ($product_id) {

        // Upload product images
    $gallery_images = [];
        if ( !empty($_FILES['productImage1']) || 
       !empty($_FILES['productImage2']) || 
       !empty($_FILES['productImage3']) || 
       !empty($_FILES['productImage4']) ) 
    {			
      // These files need to be included as dependencies when on the front end.
      if ( !function_exists('media_handle_upload') ) {
        require_once(ABSPATH . "wp-admin" . '/includes/image.php');
        require_once(ABSPATH . "wp-admin" . '/includes/file.php');
        require_once(ABSPATH . "wp-admin" . '/includes/media.php');
      }	

            foreach ($_FILES as $name => $file ) {

        $attachment_id = media_handle_upload( $name, $product_id);

        if (is_wp_error($attachment_id)) {
          continue;
        }

        if ($name == 'productImage1' ) {
          set_post_thumbnail($product_id, $attachment_id);
        } else {
          $gallery_images[] = $attachment_id;
        }
            }

            if (!empty($gallery_images)) {
                update_post_meta($product_id, '_product_image_gallery', implode(',', $gallery_images));
            }
        }	

    // Set custom taxonomy: brand, colour, size
    wp_set_post_terms( $product_id, array($productBrand), 'brand' );
    wp_set_post_terms( $product_id, array($productSize), 'size' );
    wp_set_post_terms( $product_id, array($productColour), 'colour' );

        //Set custom taxonomy: user_owner		
        if($user_id != 0)
        {
            $term_id = get_user_meta($user_id,'user_owner_tag', true);
            if($term_id != false && !empty($term_id)) 
            {
                wp_set_object_terms($product_id, (int)$term_id, 'user_owner',false);
            } 
        } 

    $productCategoryName = wp_get_post_terms( $product_id, 'product_cat', array( 'fields' => 'names' ) );
    $productCategoryName = !empty( $productCategoryName ) ? implode( ",", $productCategoryName ) : '';

    $productBrandName = wp_get_post_terms( $product_id, 'brand', array( 'fields' => 'names' ) );
    $productBrandName = !empty( $productBrandName ) ? implode( ",", $productBrandName ) : '';

    $productSizeName = wp_get_post_terms( $product_id, 'size', array( 'fields' => 'names' ) );
    $productSizeName = !empty( $productSizeName ) ? implode( ",", $productSizeName ) : '';

    $productColourName = wp_get_post_terms( $product_id, 'colour', array( 'fields' => 'names' ) );
    $productColourName = !empty( $productColourName ) ? implode( ",", $productColourName ) : '';

    $product_info = [
      'cic_logo'			=> get_site_url().'/wp-content/themes/circular-couture/assets/images/cic-logo.png',
      'user_email'		=> $user_email,
      'user_display_name'	=> $user_display_name,
      'productId' 		=> $product_id,
      'productName'		=> $productName,
      'productImage'		=> get_the_post_thumbnail_url( $product_id, 'full' ),
      'productCategory'	=> $productCategoryName,
      'productBrand'		=> $productBrandName,
      'productSize'		=> $productSizeName,
      'productColour'		=> $productColourName,
      'productRRP'		=> $retailPrice,
      'productPrice'		=> $productPrice,
      'productPreview'	=> get_site_url(). '/list-an-item?product_id='.$product_id,
      'rentalPrice4Day'	=> $rentalPrice4Day,
      'rentalPrice8Day'	=> $rentalPrice8Day,
      'rentalPrice16Day'	=> $rentalPrice16Day,
      'rentalPrice30Day'	=> $rentalPrice30Day,
      'message_for_admin'	=> 'A new product has just been published from the user: '
    ];		

    // Send mail for: Admin
    ob_start();
    wc_get_template("emails/admin-alert-product.php", $product_info ); 
    $title = 'New product created by a user owner';
    $body  = ob_get_contents();
    ob_end_clean();
    $admin_email = get_option('admin_email');
    wp_mail($admin_email, $title, $body, "Content-type: text/html; charset=utf-8");		

    // Send mail for: User
    ob_start();
    wc_get_template("emails/user-owner-alert-product.php", $product_info ); 
    $title 	= 'Product created successfully!';
    $body 	= ob_get_contents();
    ob_end_clean();
    wp_mail($user_email, $title, $body, "Content-type: text/html; charset=utf-8");	

    // Respond with success message
    wp_send_json_success(array('message' => 'Product created successfully!', 'product_id' => $product_id));
        
  }else{
    wp_send_json_error(array('message' => 'Product submission failed.'));
  }
}

 

 

 

Bài viết liên quan

post-no-image

Add the Meta Box Upload Multiple Images and multiple metabox

post-no-image

Add the Meta Box Repeat

post-no-image

Kỹ thuật debounce trong javascript – Trì hoãn nhập từ khóa trong ô input

post-no-image

Thêm VS Code snippets

post-no-image

Query only seach by title

post-no-image

Chia sẻ một số thư viện loading css đẹp