~/icsd.ir — bash
SYSTEM_ONLINE

📦 فصل ۲: مدیریت پیشرفته محصولات

قلب هر فروشگاه، محصولاتش هستند. ووکامرس انواع مختلف محصول را پشتیبانی می‌کند و در این فصل از محصول ساده تا محصول متغیر، گروهی، خارجی، دانلودی و ساخت ویژگی‌های سفارشی پیش می‌رویم. مثال‌های واقعی از صنعت فرش (ICSD) ارائه می‌شود.

قلب هر فروشگاه، محصولاتش هستند. ووکامرس انواع مختلف محصول را پشتیبانی می‌کند و در این فصل از محصول ساده تا محصول متغیر، گروهی، خارجی، دانلودی و ساخت ویژگی‌های سفارشی پیش می‌رویم. مثال‌های واقعی از صنعت فرش (ICSD) ارائه می‌شود.

۱. انواع محصول در ووکامرس

نوع کاربرد مثال
Simple محصول ساده با یک قیمت کتاب، لپتاپ، فرش با ابعاد ثابت
Variable محصول با چند نسخه پیراهن (سایز/رنگ)، فرش (ابعاد متفاوت)
Grouped چند محصول مرتبط پک نوشت‌افزار (دفتر + قلم + تراش)
External لینک به فروشگاه دیگر افیلیت دیجی‌کالا/Amazon
Virtual محصول غیرفیزیکی مشاوره، آموزش حضوری
Downloadable قابل دانلود کتاب الکترونیکی، نرم‌افزار، تم

۲. محصول ساده

ساده‌ترین نوع محصول. مدیریت موجودی، قیمت و ویژگی‌های پایه.

نکات کلیدی

  • SKU: کد یکتای محصول – برای انبارداری ضروری
  • Regular Price / Sale Price: قیمت اصلی و تخفیفی
  • Stock Management: مدیریت موجودی فعال شود
  • Weight & Dimensions: برای محاسبه هزینه ارسال

ساخت محصول با کد

create-product.php
function icsd_create_simple_product() {
    $product = new WC_Product_Simple();
    
    $product->set_name( 'فرش دستباف کاشان ۲×۳ متری' );
    $product->set_slug( 'kashan-handmade-carpet-2x3' );
    $product->set_status( 'publish' );
    $product->set_catalog_visibility( 'visible' );
    $product->set_description( 'فرش دستباف ابریشم کاشان با طرح لچک ترنج...' );
    $product->set_short_description( 'فرش دستباف اصیل کاشان' );
    $product->set_sku( 'CARPET-KSH-2X3-001' );
    $product->set_regular_price( '15000000' );
    $product->set_sale_price( '13500000' );
    $product->set_manage_stock( true );
    $product->set_stock_quantity( 5 );
    $product->set_stock_status( 'instock' );
    $product->set_weight( '8' ); // کیلوگرم
    $product->set_length( '300' ); // سانتی‌متر
    $product->set_width( '200' );
    $product->set_height( '2' );
    
    // دسته‌بندی
    $product->set_category_ids( [ 12, 15 ] ); // ID دسته‌ها
    
    // تگ
    $product->set_tag_ids( [ 8, 22 ] );
    
    // تصویر شاخص
    $product->set_image_id( 245 );
    $product->set_gallery_image_ids( [ 246, 247, 248 ] );
    
    $product_id = $product->save();
    
    return $product_id;
}

۳. محصول متغیر (مهم‌ترین نوع) ⭐

محصول متغیر یعنی یک محصول با چندین واریشن (نسخه)؛ هر واریشن قیمت، موجودی، تصویر و SKU جداگانه دارد.

📌 مثال واقعی ICSD: یک طرح فرش با ابعاد ۱.۵×۱، ۲×۳، ۳×۴ و ۴×۶ – هر کدام قیمت متفاوت. این یک محصول متغیر است.

مرحله ۱: تعریف ویژگی‌ها (Attributes)

ابتدا در Products → Attributes ویژگی «ابعاد» را تعریف کنید:

Products → Attributes
Name: ابعاد
Slug: pa_size
Enable Archives: ✓
Default sort order: Custom ordering

سپس مقادیر:
- ۱.۵ × ۱ متر (slug: 1-5x1)
- ۲ × ۳ متر (slug: 2x3)
- ۳ × ۴ متر (slug: 3x4)
- ۴ × ۶ متر (slug: 4x6)

مرحله ۲: ساخت محصول متغیر با کد

create-variable-product.php
function icsd_create_variable_carpet() {
    // 1. ساخت محصول والد
    $product = new WC_Product_Variable();
    $product->set_name( 'فرش ماشینی ۷۰۰ شانه طرح ترنج' );
    $product->set_status( 'publish' );
    $product->set_sku( 'CARPET-700-TARANJ' );
    $product->set_description( 'فرش ماشینی با کیفیت اکریلیک ۷۰۰ شانه...' );
    
    // 2. تعریف ویژگی‌ها
    $attribute = new WC_Product_Attribute();
    $attribute->set_id( wc_attribute_taxonomy_id_by_name( 'pa_size' ) );
    $attribute->set_name( 'pa_size' );
    $attribute->set_options( [ 
        get_term_by('slug', '1-5x1', 'pa_size')->term_id,
        get_term_by('slug', '2x3', 'pa_size')->term_id,
        get_term_by('slug', '3x4', 'pa_size')->term_id,
        get_term_by('slug', '4x6', 'pa_size')->term_id,
    ] );
    $attribute->set_position( 0 );
    $attribute->set_visible( true );
    $attribute->set_variation( true );  // مهم!
    
    $product->set_attributes( [ $attribute ] );
    $product_id = $product->save();
    
    // 3. ساخت واریشن‌ها
    $variations = [
        [ 'size' => '1-5x1', 'price' => 1500000, 'sku' => 'CARPET-700-1-5X1', 'stock' => 10 ],
        [ 'size' => '2x3',   'price' => 4500000, 'sku' => 'CARPET-700-2X3',   'stock' => 8 ],
        [ 'size' => '3x4',   'price' => 9000000, 'sku' => 'CARPET-700-3X4',   'stock' => 5 ],
        [ 'size' => '4x6',   'price' => 18000000,'sku' => 'CARPET-700-4X6',   'stock' => 3 ],
    ];
    
    foreach ( $variations as $var_data ) {
        $variation = new WC_Product_Variation();
        $variation->set_parent_id( $product_id );
        $variation->set_attributes( [ 'pa_size' => $var_data['size'] ] );
        $variation->set_regular_price( $var_data['price'] );
        $variation->set_sku( $var_data['sku'] );
        $variation->set_manage_stock( true );
        $variation->set_stock_quantity( $var_data['stock'] );
        $variation->set_stock_status( 'instock' );
        $variation->save();
    }
    
    // 4. به‌روزرسانی محصول والد
    WC_Product_Variable::sync( $product_id );
    
    return $product_id;
}

دریافت تمام واریشن‌های یک محصول

get-variations.php
function icsd_get_product_variations( $product_id ) {
    $product = wc_get_product( $product_id );
    
    if ( ! $product || ! $product->is_type( 'variable' ) ) {
        return [];
    }
    
    $variations = [];
    
    foreach ( $product->get_children() as $variation_id ) {
        $variation = wc_get_product( $variation_id );
        
        $variations[] = [
            'id'         => $variation_id,
            'sku'        => $variation->get_sku(),
            'price'      => $variation->get_price(),
            'stock'      => $variation->get_stock_quantity(),
            'attributes' => $variation->get_variation_attributes(),
            'image'      => wp_get_attachment_url( $variation->get_image_id() ),
        ];
    }
    
    return $variations;
}

۴. محصول گروهی

محصول گروهی، چند محصول ساده را به عنوان یک «گروه» نمایش می‌دهد. مثلاً پک «فرش + پاندا + کاور» – مشتری می‌تواند هر کدام را جداگانه به سبد اضافه کند.

grouped-product.php
function icsd_create_grouped_product( $children_ids ) {
    $product = new WC_Product_Grouped();
    $product->set_name( 'پک کامل لوازم دفتری' );
    $product->set_status( 'publish' );
    $product->set_sku( 'OFFICE-PACK-001' );
    $product->set_children( $children_ids ); // آرایه ID محصولات ساده
    
    return $product->save();
}

// استفاده:
icsd_create_grouped_product( [ 100, 101, 102, 103 ] );

۵. محصول خارجی (Affiliate)

محصول خارجی به جای دکمه «افزودن به سبد»، دکمه «خرید از فروشگاه فروشنده» نمایش می‌دهد و لینک به سایت دیگر می‌دهد.

external-product.php
$product = new WC_Product_External();
$product->set_name( 'لپتاپ ASUS - خرید از دیجی‌کالا' );
$product->set_regular_price( '45000000' );
$product->set_product_url( 'https://www.digikala.com/product/dkp-12345?aff=icsd' );
$product->set_button_text( 'خرید از دیجی‌کالا' );
$product->save();

۶. محصولات مجازی و دانلودی

محصول مجازی (Virtual)

محصولی که نیاز به ارسال فیزیکی ندارد (مثل مشاوره، خدمات، عضویت).

محصول دانلودی (Downloadable)

downloadable-product.php
$product = new WC_Product_Simple();
$product->set_name( 'کتاب الکترونیکی هنر فرش‌بافی کاشان' );
$product->set_regular_price( '85000' );
$product->set_virtual( true );
$product->set_downloadable( true );

// تنظیم فایل دانلودی
$downloads = [];
$download = new WC_Product_Download();
$download->set_id( wp_generate_uuid4() );
$download->set_name( 'فایل PDF' );
$download->set_file( 'https://icsd.ir/downloads/secure/carpet-art.pdf' );
$downloads[] = $download;

$product->set_downloads( $downloads );
$product->set_download_limit( 3 );      // حداکثر ۳ بار دانلود
$product->set_download_expiry( 30 );    // اعتبار ۳۰ روز
$product->save();
⚠️ امنیت دانلود: فایل‌های دانلودی را در پوشه‌ای خارج از public_html نگه دارید و از X-Sendfile یا تنظیمات WooCommerce برای جلوگیری از دانلود مستقیم استفاده کنید. تنظیمات: WooCommerce → Settings → Products → Downloadable products → File download method: Force downloads.

۷. ویژگی‌ها و واریشن‌ها

دو نوع ویژگی

نوع کاربرد قابل استفاده در واریشن
Global Attribute قابل استفاده در چندین محصول (پیشنهادی) بله ✓
Custom Attribute فقط برای یک محصول بله، اما توصیه نمی‌شود

ساخت Global Attribute با کد

create-attribute.php
function icsd_register_attribute( $name, $slug ) {
    if ( wc_attribute_taxonomy_id_by_name( $slug ) ) {
        return; // قبلاً ثبت شده
    }
    
    $args = [
        'attribute_label'   => $name,
        'attribute_name'    => $slug,
        'attribute_type'    => 'select',
        'attribute_orderby' => 'menu_order',
        'attribute_public'  => 1,
    ];
    
    wc_create_attribute( $args );
    
    // ثبت taxonomy
    register_taxonomy( 'pa_' . $slug, 'product', [
        'hierarchical' => false,
        'show_ui'      => false,
        'query_var'    => true,
        'rewrite'      => false,
    ]);
}

// استفاده
icsd_register_attribute( 'جنس', 'material' );
icsd_register_attribute( 'تعداد رج', 'rajshomar' );

// افزودن مقادیر
wp_insert_term( 'ابریشم', 'pa_material' );
wp_insert_term( 'پشم', 'pa_material' );
wp_insert_term( 'اکریلیک', 'pa_material' );

wp_insert_term( '۷۰۰ شانه', 'pa_rajshomar' );
wp_insert_term( '۱۲۰۰ شانه', 'pa_rajshomar' );

۸. فیلدهای سفارشی محصول

گاهی نیاز به فیلدهای اختصاصی دارید که در WooCommerce نیست؛ مثل «تاریخ تولید»، «گارانتی»، «شناسه بافنده» (برای فرش).

روش ۱: استفاده از ACF (سریع‌ترین)

افزونه Advanced Custom Fields را نصب کنید، سپس Custom Fields → Add New Field Group با Location: Post Type = Product.

روش ۲: کد سفارشی برای متاباکس

includes/class-product-meta.php
<?php
class ICSD_Product_Custom_Fields {
    
    public function __construct() {
        // افزودن تب در ادیتور محصول
        add_filter( 'woocommerce_product_data_tabs', [ $this, 'add_tab' ] );
        add_action( 'woocommerce_product_data_panels', [ $this, 'add_panel' ] );
        add_action( 'woocommerce_process_product_meta', [ $this, 'save_fields' ] );
    }
    
    public function add_tab( $tabs ) {
        $tabs['icsd_carpet'] = [
            'label'  => 'اطلاعات فرش',
            'target' => 'icsd_carpet_data',
            'class'  => [ 'show_if_simple', 'show_if_variable' ],
            'priority' => 21,
        ];
        return $tabs;
    }
    
    public function add_panel() {
        global $post;
        ?>
        
'_icsd_weaver_name', 'label' => 'نام بافنده', 'placeholder' => 'استاد...', 'desc_tip' => true, 'description' => 'نام استاد بافنده فرش', ]); woocommerce_wp_select([ 'id' => '_icsd_origin', 'label' => 'اصالت', 'options' => [ 'kashan' => 'کاشان', 'tabriz' => 'تبریز', 'isfahan' => 'اصفهان', 'qom' => 'قم', 'mashhad' => 'مشهد', ], ]); woocommerce_wp_text_input([ 'id' => '_icsd_knots_per_meter', 'label' => 'تعداد گره در متر', 'type' => 'number', 'desc_tip' => true, 'description' => 'تراکم گره‌بافی', ]); woocommerce_wp_textarea_input([ 'id' => '_icsd_design_story', 'label' => 'داستان طرح', ]); ?>
'sanitize_text_field', '_icsd_origin' => 'sanitize_key', '_icsd_knots_per_meter' => 'absint', '_icsd_design_story' => 'sanitize_textarea_field', ]; foreach ( $fields as $key => $sanitizer ) { if ( isset( $_POST[ $key ] ) ) { $value = call_user_func( $sanitizer, wp_unslash( $_POST[ $key ] ) ); update_post_meta( $post_id, $key, $value ); } } } } new ICSD_Product_Custom_Fields();

نمایش فیلدها در صفحه محصول

functions.php
add_action( 'woocommerce_single_product_summary', 'icsd_show_carpet_info', 25 );

function icsd_show_carpet_info() {
    global $product;
    
    $weaver = get_post_meta( $product->get_id(), '_icsd_weaver_name', true );
    $origin = get_post_meta( $product->get_id(), '_icsd_origin', true );
    $knots  = get_post_meta( $product->get_id(), '_icsd_knots_per_meter', true );
    
    if ( ! $weaver && ! $origin ) {
        return;
    }
    
    echo '
'; if ( $weaver ) echo '

بافنده: ' . esc_html( $weaver ) . '

'; if ( $origin ) echo '

اصالت: ' . esc_html( $origin ) . '

'; if ( $knots ) echo '

گره در متر: ' . number_format( $knots ) . '

'; echo '
'; }

۹. ورود انبوه محصولات با CSV

ووکامرس import/export داخلی دارد ولی برای حجم بالا، اسکریپت اختصاصی بهتر است.

bulk-import.php
function icsd_bulk_import_products( $csv_file ) {
    if ( ! file_exists( $csv_file ) ) {
        return new WP_Error( 'no_file', 'فایل پیدا نشد' );
    }
    
    $imported = 0;
    $errors = [];
    
    $handle = fopen( $csv_file, 'r' );
    $headers = fgetcsv( $handle ); // اولین ردیف = هدر
    
    while ( ( $row = fgetcsv( $handle ) ) !== false ) {
        $data = array_combine( $headers, $row );
        
        try {
            $product = new WC_Product_Simple();
            $product->set_name( $data['name'] );
            $product->set_sku( $data['sku'] );
            $product->set_regular_price( $data['price'] );
            $product->set_description( $data['description'] );
            $product->set_stock_quantity( (int) $data['stock'] );
            $product->set_manage_stock( true );
            $product->set_status( 'publish' );
            
            // دسته‌بندی
            if ( ! empty( $data['category'] ) ) {
                $term = get_term_by( 'name', $data['category'], 'product_cat' );
                if ( $term ) {
                    $product->set_category_ids( [ $term->term_id ] );
                }
            }
            
            $product->save();
            $imported++;
            
        } catch ( Exception $e ) {
            $errors[] = "خطا در سطر {$imported}: " . $e->getMessage();
        }
    }
    
    fclose( $handle );
    
    return [
        'imported' => $imported,
        'errors'   => $errors,
    ];
}

// اجرا با WP-CLI
// wp eval-file bulk-import.php

📝 مثال‌های عملی

🟢 آسان: تخفیف خودکار محصولات یک دسته

functions.php
function icsd_apply_category_discount( $category_slug, $discount_percent ) {
    $args = [
        'post_type'      => 'product',
        'posts_per_page' => -1,
        'tax_query'      => [
            [
                'taxonomy' => 'product_cat',
                'field'    => 'slug',
                'terms'    => $category_slug,
            ],
        ],
    ];
    
    $query = new WP_Query( $args );
    
    while ( $query->have_posts() ) {
        $query->the_post();
        $product = wc_get_product( get_the_ID() );
        
        $regular = (float) $product->get_regular_price();
        $sale = $regular * ( 1 - $discount_percent / 100 );
        
        $product->set_sale_price( round( $sale ) );
        $product->save();
    }
    
    wp_reset_postdata();
}

// تخفیف ۲۰٪ روی فرش‌های ماشینی
icsd_apply_category_discount( 'machine-carpet', 20 );

🟡 متوسط: محاسبه قیمت بر اساس متراژ (فرش متراژی)

functions.php
// افزودن فیلد متراژ به صفحه محصول
add_action( 'woocommerce_before_add_to_cart_button', 'icsd_add_meter_field' );
function icsd_add_meter_field() {
    global $product;
    if ( ! has_term( 'meter-carpet', 'product_cat', $product->get_id() ) ) {
        return;
    }
    ?>
    

قیمت هر متر: get_price() ); ?>

get_cart() as $cart_item ) { if ( ! isset( $cart_item['icsd_meter'] ) ) continue; $base_price = $cart_item['data']->get_regular_price(); $new_price = $base_price * $cart_item['icsd_meter']; $cart_item['data']->set_price( $new_price ); } }

🔴 پیشرفته: محصول دینامیک با تنظیمات دلخواه

includes/class-custom-product.php
/**
 * محصول سفارشی فرش با گزینه‌های شخصی‌سازی
 * - انتخاب رنگ زمینه
 * - انتخاب طرح حاشیه
 * - حک نام مشتری روی برچسب
 */
class ICSD_Customizable_Carpet {
    
    public function __construct() {
        add_action( 'woocommerce_before_add_to_cart_button', [ $this, 'render_options' ] );
        add_filter( 'woocommerce_add_cart_item_data', [ $this, 'save_options' ], 10, 2 );
        add_filter( 'woocommerce_get_item_data', [ $this, 'display_options' ], 10, 2 );
        add_action( 'woocommerce_checkout_create_order_line_item', [ $this, 'save_to_order' ], 10, 4 );
        add_action( 'woocommerce_before_calculate_totals', [ $this, 'apply_custom_price' ] );
    }
    
    public function render_options() {
        global $product;
        if ( ! $product->is_type( 'simple' ) ) return;
        if ( ! get_post_meta( $product->get_id(), '_is_customizable', true ) ) return;
        ?>
        

'کرم', 'navy' => 'سرمه‌ای', 'red' => 'قرمز' ]; if ( ! empty( $cart_item['bg_color'] ) ) { $item_data[] = [ 'name' => 'رنگ زمینه', 'value' => $color_labels[ $cart_item['bg_color'] ] ?? '', ]; } if ( ! empty( $cart_item['custom_text'] ) ) { $item_data[] = [ 'name' => 'متن سفارشی', 'value' => $cart_item['custom_text'], ]; } return $item_data; } public function apply_custom_price( $cart ) { $color_extra = [ 'cream' => 0, 'navy' => 500000, 'red' => 800000 ]; foreach ( $cart->get_cart() as $cart_item ) { if ( empty( $cart_item['bg_color'] ) ) continue; $base = $cart_item['data']->get_regular_price(); $extra = $color_extra[ $cart_item['bg_color'] ] ?? 0; // اگر متن سفارشی هم خواست if ( ! empty( $cart_item['custom_text'] ) ) { $extra += 200000; } $cart_item['data']->set_price( $base + $extra ); } } public function save_to_order( $item, $cart_item_key, $values, $order ) { if ( ! empty( $values['bg_color'] ) ) { $item->add_meta_data( 'رنگ زمینه', $values['bg_color'] ); } if ( ! empty( $values['custom_text'] ) ) { $item->add_meta_data( 'متن سفارشی', $values['custom_text'] ); } } } new ICSD_Customizable_Carpet();

📌 خلاصه فصل

  • ✅ ۶ نوع محصول در ووکامرس وجود دارد – Variable پرکاربردترین برای صنعت ایران
  • ✅ همیشه از Global Attribute به جای Custom Attribute استفاده کنید (قابل reuse)
  • HPOS-compatible کد بنویسید: همیشه از wc_get_product() و CRUD
  • ✅ برای فیلدهای سفارشی: ACF برای سرعت، متاباکس کدنویسی برای انعطاف
  • ✅ برای واردات انبوه از CSV استفاده کنید + اسکریپت سفارشی برای اعتبارسنجی
  • ✅ از woocommerce_before_calculate_totals برای قیمت‌گذاری دینامیک استفاده کنید
🎯 در فصل بعد: به سراغ سفارش‌ها و فرآیند پرداخت می‌رویم – چرخه حیات سفارش، HPOS عمیق، سفارشی‌سازی Checkout و وضعیت‌های اختصاصی.

نمایش سایت

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

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