~/icsd.ir — bash
SYSTEM_ONLINE

🎁 فصل ۸: Custom Product Type – نوع محصول سفارشی

ووکامرس به‌صورت پیش‌فرض ۴ نوع محصول دارد (ساده، متغیر، گروهی، خارجی). در این فصل یاد می‌گیرید چطور نوع محصول کاملاً جدید بسازید — مثلاً «فرش سفارشی» با اندازه دلخواه یا «کارت هدیه».

🤔 چرا Custom Product Type؟

گاهی محصول شما با ساده/متغیر سازگار نیست:

نیاز راه‌حل ووکامرس پیش‌فرض راه‌حل بهینه
فرش با اندازه دلخواه (طول × عرض) ندارد! Custom Product Type
کارت هدیه با مبلغ دلخواه متغیر (محدود) Custom Product Type
محصول قابل پیکربندی (PC build) افزونه Composite Custom Product Type
اشتراک ماهانه WC Subscriptions Custom Product Type

🏗️ ساخت کلاس محصول

هر نوع محصول جدید باید کلاس WC_Product یا WC_Product_Simple را extend کند:

includes/class-wc-product-custom-carpet.php
<?php
defined('ABSPATH') || exit;

class WC_Product_Custom_Carpet extends WC_Product {
    
    public function __construct($product = 0) {
        $this->product_type = 'custom_carpet';
        parent::__construct($product);
    }
    
    public function get_type() {
        return 'custom_carpet';
    }
    
    /**
     * این محصول قابل خرید است
     */
    public function is_purchasable() {
        return true;
    }
    
    /**
     * موجودی محدود ندارد - بر اساس سفارش بافته می‌شود
     */
    public function is_in_stock() {
        return true;
    }
    
    /**
     * قیمت متناسب با اندازه محاسبه می‌شود
     */
    public function get_price($context = 'view') {
        $base_price = parent::get_price($context);
        return $base_price; // قیمت پایه - محاسبه واقعی در سبد
    }
    
    /**
     * تنظیمات اختصاصی محصول
     */
    public function get_price_per_meter() {
        return floatval($this->get_meta('_price_per_meter'));
    }
    
    public function get_min_size() {
        return floatval($this->get_meta('_min_size_m2') ?: 1);
    }
    
    public function get_max_size() {
        return floatval($this->get_meta('_max_size_m2') ?: 50);
    }
    
    public function get_production_days() {
        return absint($this->get_meta('_production_days') ?: 30);
    }
}

📝 ثبت در ووکامرس

custom-carpet-product-type.php
<?php
/**
 * Plugin Name: ICSD Custom Carpet Product Type
 */

defined('ABSPATH') || exit;

// 1. اضافه کردن به لیست انواع محصول
add_filter('product_type_selector', function($types) {
    $types['custom_carpet'] = 'فرش سفارشی';
    return $types;
});

// 2. ثبت taxonomy term (برای فیلتر و query)
add_action('init', function() {
    if (!term_exists('custom_carpet', 'product_type')) {
        wp_insert_term('custom_carpet', 'product_type');
    }
});

// 3. ثبت کلاس
add_filter('woocommerce_product_class', function($classname, $product_type) {
    if ($product_type === 'custom_carpet') {
        require_once __DIR__ . '/includes/class-wc-product-custom-carpet.php';
        return 'WC_Product_Custom_Carpet';
    }
    return $classname;
}, 10, 2);

// 4. لود ساختار در فرانت‌اند
add_action('woocommerce_single_product_summary', 'render_custom_carpet_form', 25);

function render_custom_carpet_form() {
    global $product;
    if ($product->get_type() !== 'custom_carpet') return;
    require __DIR__ . '/templates/single-product/custom-carpet.php';
}

⚙️ تب‌های پنل ادمین

برای اینکه ادمین بتواند تنظیمات این نوع محصول را بدهد، باید تب اختصاصی اضافه کنیم:

// 1. اضافه کردن تب
add_filter('woocommerce_product_data_tabs', function($tabs) {
    $tabs['custom_carpet'] = [
        'label'    => '🧵 تنظیمات فرش سفارشی',
        'target'   => 'custom_carpet_options',
        'class'    => ['show_if_custom_carpet'],
        'priority' => 25,
    ];
    return $tabs;
});

// 2. محتوای تب
add_action('woocommerce_product_data_panels', function() {
    global $post;
    ?>
    
'_price_per_meter', 'label' => 'قیمت هر متر مربع (تومان)', 'type' => 'number', 'desc_tip' => true, 'description' => 'مبنای محاسبه قیمت - بر حسب متراژ', ]); woocommerce_wp_text_input([ 'id' => '_min_size_m2', 'label' => 'حداقل اندازه (متر مربع)', 'type' => 'number', 'value' => get_post_meta($post->ID, '_min_size_m2', true) ?: '1', ]); woocommerce_wp_text_input([ 'id' => '_max_size_m2', 'label' => 'حداکثر اندازه (متر مربع)', 'type' => 'number', 'value' => get_post_meta($post->ID, '_max_size_m2', true) ?: '50', ]); woocommerce_wp_text_input([ 'id' => '_production_days', 'label' => 'مدت تولید (روز)', 'type' => 'number', 'value' => get_post_meta($post->ID, '_production_days', true) ?: '30', ]); woocommerce_wp_select([ 'id' => '_design_complexity', 'label' => 'پیچیدگی نقشه', 'options' => [ 'simple' => 'ساده', 'medium' => 'متوسط', 'complex' => 'پیچیده', ], ]); woocommerce_wp_textarea_input([ 'id' => '_color_options', 'label' => 'رنگ‌بندی موجود (هر خط یک رنگ)', 'rows' => 5, ]); ?>
get_type() !== 'custom_carpet') return; $fields = [ '_price_per_meter', '_min_size_m2', '_max_size_m2', '_production_days', '_design_complexity', '_color_options', ]; foreach ($fields as $field) { if (isset($_POST[$field])) { $product->update_meta_data($field, sanitize_text_field($_POST[$field])); } } }); // 4. نمایش/مخفی کردن تب‌های پیش‌فرض با CSS add_action('admin_footer', function() { global $post; if (!$post || $post->post_type !== 'product') return; ?>

🖼️ رفتار در فرانت‌اند

فرم انتخاب اندازه و رنگ:

templates/single-product/custom-carpet.php
<?php
defined('ABSPATH') || exit;
global $product;

$price_per_meter = $product->get_price_per_meter();
$min_size = $product->get_min_size();
$max_size = $product->get_max_size();
$colors = explode("n", $product->get_meta('_color_options'));
?>

📐 اندازه دلخواه شما

متراژ: ۶ متر مربع قیمت:

🎨 رنگ‌بندی

📁 نقشه یا الگوی دلخواه (اختیاری)

⏱ مدت تولید: get_production_days() ?> روز

📦 ارسال رایگان به سراسر کشور

🛒 سبد و پرداخت

وقتی محصول به سبد اضافه می‌شود، باید قیمت بر اساس اندازه محاسبه شود:

// 1. اضافه کردن داده‌های سفارشی به سبد
add_filter('woocommerce_add_cart_item_data', function($cart_item_data, $product_id) {
    $product = wc_get_product($product_id);
    if ($product->get_type() !== 'custom_carpet') return $cart_item_data;
    
    $length = floatval($_POST['carpet_length'] ?? 0);
    $width  = floatval($_POST['carpet_width'] ?? 0);
    $area   = $length * $width;
    
    $cart_item_data['custom_carpet'] = [
        'length' => $length,
        'width'  => $width,
        'area'   => $area,
        'color'  => sanitize_text_field($_POST['carpet_color'] ?? ''),
        'notes'  => sanitize_textarea_field($_POST['carpet_notes'] ?? ''),
    ];
    
    // آپلود فایل
    if (!empty($_FILES['carpet_design']['name'])) {
        require_once ABSPATH . 'wp-admin/includes/file.php';
        $upload = wp_handle_upload($_FILES['carpet_design'], ['test_form' => false]);
        if (!isset($upload['error'])) {
            $cart_item_data['custom_carpet']['design_url'] = $upload['url'];
        }
    }
    
    // یونیک کردن کلید (هر اندازه یک ردیف جدا)
    $cart_item_data['unique_key'] = md5(microtime() . rand());
    
    return $cart_item_data;
}, 10, 2);

// 2. محاسبه قیمت بر اساس اندازه
add_action('woocommerce_before_calculate_totals', function($cart) {
    if (is_admin() && !defined('DOING_AJAX')) return;
    
    foreach ($cart->get_cart() as $item) {
        if (empty($item['custom_carpet'])) continue;
        
        $product = $item['data'];
        if ($product->get_type() !== 'custom_carpet') continue;
        
        $price_per_meter = floatval($product->get_meta('_price_per_meter'));
        $area = floatval($item['custom_carpet']['area']);
        $new_price = $price_per_meter * $area;
        
        $product->set_price($new_price);
    }
});

// 3. نمایش جزئیات در سبد
add_filter('woocommerce_get_item_data', function($item_data, $cart_item) {
    if (empty($cart_item['custom_carpet'])) return $item_data;
    
    $cc = $cart_item['custom_carpet'];
    $item_data[] = ['key' => 'اندازه', 'value' => sprintf('%s × %s متر (%s متر مربع)', 
                    $cc['length'], $cc['width'], $cc['area'])];
    $item_data[] = ['key' => 'رنگ', 'value' => $cc['color']];
    
    if (!empty($cc['notes'])) {
        $item_data[] = ['key' => 'توضیحات', 'value' => $cc['notes']];
    }
    if (!empty($cc['design_url'])) {
        $item_data[] = ['key' => 'نقشه دلخواه', 
                        'value' => 'مشاهده فایل'];
    }
    
    return $item_data;
}, 10, 2);

// 4. ذخیره در آیتم سفارش
add_action('woocommerce_checkout_create_order_line_item', function($item, $cart_item_key, $values) {
    if (empty($values['custom_carpet'])) return;
    
    $cc = $values['custom_carpet'];
    $item->add_meta_data('اندازه', sprintf('%s × %s', $cc['length'], $cc['width']));
    $item->add_meta_data('متراژ', $cc['area'] . ' مربع');
    $item->add_meta_data('رنگ', $cc['color']);
    if (!empty($cc['notes'])) $item->add_meta_data('توضیحات', $cc['notes']);
    if (!empty($cc['design_url'])) $item->add_meta_data('فایل نقشه', $cc['design_url']);
}, 10, 3);

🎯 مثال کامل: فرش سفارشی

تصویر کامل عملکرد:

سناریو واقعی برای ICSD:

  1. ادمین یک محصول از نوع «فرش سفارشی» می‌سازد، قیمت هر مربع را ۲ میلیون تومان می‌گذارد
  2. کاربر در فرانت‌اند طول ۳ و عرض ۴ متر انتخاب می‌کند
  3. JavaScript قیمت را ۲۴ میلیون تومان نمایش می‌دهد
  4. کاربر رنگ «شیری» را انتخاب و فایل نقشه را آپلود می‌کند
  5. محصول با تمام جزئیات به سبد اضافه می‌شود
  6. در پنل ادمین، تمام مشخصات قابل مشاهده است
  7. بعد از پرداخت، تولید آغاز می‌شود

اضافه کردن وضعیت سفارش «در حال بافت»

add_action('init', function() {
    register_post_status('wc-weaving', [
        'label'                     => 'در حال بافت',
        'public'                    => true,
        'show_in_admin_status_list' => true,
        'show_in_admin_all_list'    => true,
        'label_count' => _n_noop('در حال بافت (%s)',
                                  'در حال بافت (%s)'),
    ]);
});

add_filter('wc_order_statuses', function($statuses) {
    $new = [];
    foreach ($statuses as $key => $label) {
        $new[$key] = $label;
        if ($key === 'wc-processing') {
            $new['wc-weaving'] = 'در حال بافت';
        }
    }
    return $new;
});

📝 خلاصه فصل

  • ساخت کلاس Custom Product Type با extend از WC_Product
  • ثبت در product_type_selector و mapping با کلاس
  • تب اختصاصی در پنل ادمین با woocommerce_wp_*
  • قالب فرانت‌اند با محاسبه پویا قیمت
  • ذخیره داده‌های سفارشی در سبد و آیتم سفارش
  • وضعیت سفارش سفارشی (مثل «در حال بافت»)
🎯 ایده‌های دیگر برای Custom Product Type:

  • کارت هدیه با مبلغ دلخواه و تاریخ ارسال
  • اشتراک ماهانه قهوه (با انتخاب طعم)
  • چاپ سفارشی روی تیشرت (با آپلود طرح)
  • کیک تولد با پیام و تصویر دلخواه

نمایش سایت

رنگ سایت
حالت نمایش
اندازهٔ متن
خوانایی

این تنظیمات فقط روی مرورگر شما ذخیره می‌شود.