文章目录[隐藏]
实操指南:实现商品多属性变体接口的5个设计要点
引言:为什么商品多属性变体如此重要?
在当今的电商环境中,商品多属性变体已成为在线商店的标配功能。无论是服装的颜色和尺码组合,还是电子产品的配置选项,多属性变体系统都能显著提升用户体验和销售转化率。对于WordPress开发者而言,实现一个高效、可扩展的多属性变体接口是构建专业电商网站的关键技术挑战。
本文将从WordPress开发者的角度出发,深入探讨实现商品多属性变体接口的5个核心设计要点。我们将结合WooCommerce的实际案例,为行业新人和程序员提供可操作的开发指导。
设计要点一:建立灵活的数据结构模型
理解WooCommerce的数据结构基础
在WordPress生态中,WooCommerce是最流行的电商插件,其数据模型为我们提供了良好的参考基础。WooCommerce将产品分为简单产品、可变产品、分组产品和外部/关联产品四种类型。其中,可变产品(Variable Product)正是多属性变体的实现载体。
// WooCommerce可变产品的基本数据结构示例
class WC_Product_Variable extends WC_Product {
// 产品变体数组
protected $children = null;
// 产品属性数组
protected $attributes = array();
// 获取所有变体
public function get_children() {
// 实现逻辑
}
// 获取产品属性
public function get_attributes() {
// 实现逻辑
}
}
设计可扩展的属性系统
一个优秀的多属性系统应该具备以下特点:
- 动态属性定义:允许商家自定义属性类型(颜色、尺寸、材质等)
- 层级结构:支持属性分组和子属性
- 多语言支持:为国际化电商做好准备
- 性能优化:合理设计数据库表结构,避免过度连接查询
// 自定义属性数据表设计示例
global $wpdb;
$attribute_table = $wpdb->prefix . 'custom_attributes';
$attribute_terms_table = $wpdb->prefix . 'custom_attribute_terms';
$product_attribute_table = $wpdb->prefix . 'product_attributes';
// 创建表的SQL语句
$sql = "CREATE TABLE IF NOT EXISTS {$attribute_table} (
attribute_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
attribute_name VARCHAR(200) NOT NULL,
attribute_slug VARCHAR(200) NOT NULL,
attribute_type VARCHAR(50) DEFAULT 'select',
attribute_order INT DEFAULT 0,
PRIMARY KEY (attribute_id),
UNIQUE KEY attribute_slug (attribute_slug)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;";
实现属性与变体的关联
在数据库层面,我们需要建立产品、属性和变体之间的多对多关系。这种设计允许一个属性被多个产品使用,一个产品拥有多个属性,一个变体是特定属性值的组合。
// 变体与属性关联的中间表示例
class Product_Variant {
private $variant_id;
private $product_id;
private $attributes = array(); // 属性值对数组
private $sku;
private $price;
private $stock_quantity;
// 根据属性组合查找变体
public static function find_variant_by_attributes($product_id, $attributes) {
global $wpdb;
$query = "SELECT v.variant_id FROM {$wpdb->prefix}product_variants v
JOIN {$wpdb->prefix}variant_attributes va ON v.variant_id = va.variant_id
WHERE v.product_id = %d";
$params = array($product_id);
$conditions = array();
foreach ($attributes as $attribute_id => $value) {
$conditions[] = "(va.attribute_id = %d AND va.attribute_value = %s)";
$params[] = $attribute_id;
$params[] = $value;
}
if (!empty($conditions)) {
$query .= " AND " . implode(" AND ", $conditions);
}
$query .= " GROUP BY v.variant_id HAVING COUNT(va.id) = %d";
$params[] = count($attributes);
return $wpdb->get_var($wpdb->prepare($query, $params));
}
}
设计要点二:构建高效的前端交互界面
动态属性选择器的实现
前端交互是多属性变体系统的用户体验核心。用户选择不同属性时,系统需要实时更新可用选项、价格、库存和产品图片。
// 动态属性选择器示例(基于jQuery)
class VariantSelector {
constructor(productId, containerSelector) {
this.productId = productId;
this.container = $(containerSelector);
this.selectedAttributes = {};
this.availableVariants = {};
this.init();
}
init() {
// 获取产品变体数据
this.fetchVariants();
// 绑定属性选择事件
this.container.on('change', '.attribute-option', (e) => {
this.handleAttributeChange(e.target);
});
}
async fetchVariants() {
try {
const response = await $.ajax({
url: '/wp-json/custom-api/v1/products/' + this.productId + '/variants',
method: 'GET'
});
this.availableVariants = response.variants;
this.updateAttributeOptions();
} catch (error) {
console.error('Failed to fetch variants:', error);
}
}
handleAttributeChange(selectElement) {
const attributeId = $(selectElement).data('attribute-id');
const value = $(selectElement).val();
// 更新已选属性
if (value) {
this.selectedAttributes[attributeId] = value;
} else {
delete this.selectedAttributes[attributeId];
}
// 查找匹配的变体
const matchedVariant = this.findMatchingVariant();
// 更新UI
this.updateUI(matchedVariant);
}
findMatchingVariant() {
return Object.values(this.availableVariants).find(variant => {
return this.areAttributesMatching(variant.attributes);
});
}
areAttributesMatching(variantAttributes) {
// 检查变体属性是否与已选属性匹配
for (const [attrId, value] of Object.entries(this.selectedAttributes)) {
if (!variantAttributes[attrId] || variantAttributes[attrId] !== value) {
return false;
}
}
return true;
}
updateUI(variant) {
if (variant) {
// 更新价格
this.updatePrice(variant.price);
// 更新库存状态
this.updateStock(variant.stock_status, variant.stock_quantity);
// 更新图片
this.updateImage(variant.image_url);
// 启用添加到购物车按钮
this.enableAddToCart(true, variant.variant_id);
} else {
// 没有匹配变体,禁用添加到购物车
this.enableAddToCart(false);
}
// 更新其他属性的可用选项
this.updateAttributeOptions();
}
}
响应式变体展示优化
在移动设备上,属性选择器的布局需要特别优化。我们可以采用以下策略:
- 折叠式选择器:在移动端将属性选择器折叠,点击展开
- 滑动选择:对于颜色等可视化属性,使用滑动选择器
- 图片预览:实时显示选中属性组合的产品图片
- 渐进式加载:先加载关键属性,其他属性按需加载
/* 响应式变体选择器CSS示例 */
.variant-selector {
margin: 20px 0;
}
.attribute-group {
margin-bottom: 15px;
}
.attribute-label {
display: block;
margin-bottom: 8px;
font-weight: 600;
}
.attribute-options {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.attribute-option {
padding: 8px 16px;
border: 2px solid #ddd;
border-radius: 4px;
background: white;
cursor: pointer;
transition: all 0.2s;
}
.attribute-option.selected {
border-color: #007cba;
background-color: #f0f8ff;
}
.attribute-option.disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* 移动端优化 */
@media (max-width: 768px) {
.attribute-options {
overflow-x: auto;
flex-wrap: nowrap;
padding-bottom: 10px;
}
.attribute-option {
flex-shrink: 0;
min-width: 60px;
text-align: center;
}
.variant-image-preview {
position: sticky;
top: 0;
background: white;
z-index: 10;
}
}
设计要点三:设计高性能的后端API接口
RESTful API设计原则
为多属性变体系统设计API时,应遵循RESTful原则,确保接口的一致性和可预测性。
// WordPress REST API端点示例
class Product_Variants_REST_Controller extends WP_REST_Controller {
public function register_routes() {
// 获取产品变体列表
register_rest_route('custom-api/v1', '/products/(?P<id>[d]+)/variants', array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array($this, 'get_product_variants'),
'permission_callback' => array($this, 'get_items_permissions_check'),
'args' => array(
'id' => array(
'validate_callback' => function($param, $request, $key) {
return is_numeric($param);
}
),
),
),
array(
'methods' => WP_REST_Server::CREATABLE,
'callback' => array($this, 'create_product_variant'),
'permission_callback' => array($this, 'create_item_permissions_check'),
),
));
// 获取单个变体
register_rest_route('custom-api/v1', '/variants/(?P<id>[d]+)', array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array($this, 'get_variant'),
'permission_callback' => array($this, 'get_item_permissions_check'),
),
));
}
public function get_product_variants($request) {
$product_id = $request->get_param('id');
// 验证产品是否存在
if (!get_post($product_id) || get_post_type($product_id) !== 'product') {
return new WP_Error('rest_product_invalid', __('Invalid product ID.'), array('status' => 404));
}
// 获取变体数据
$variants = $this->fetch_variants($product_id);
// 格式化响应数据
$response_data = array(
'product_id' => $product_id,
'variants' => $variants,
'attributes' => $this->get_product_attributes($product_id),
'total' => count($variants)
);
return rest_ensure_response($response_data);
}
private function fetch_variants($product_id) {
global $wpdb;
// 使用单个查询获取所有变体数据,避免N+1查询问题
$query = $wpdb->prepare("
SELECT
v.variant_id,
v.sku,
v.price,
v.sale_price,
v.stock_quantity,
v.stock_status,
v.image_id,
GROUP_CONCAT(
CONCAT(a.attribute_slug, ':', va.attribute_value)
ORDER BY a.attribute_order
SEPARATOR '|'
) as attribute_string
FROM {$wpdb->prefix}product_variants v
LEFT JOIN {$wpdb->prefix}variant_attributes va ON v.variant_id = va.variant_id
LEFT JOIN {$wpdb->prefix}custom_attributes a ON va.attribute_id = a.attribute_id
WHERE v.product_id = %d AND v.status = 'publish'
GROUP BY v.variant_id
ORDER BY v.menu_order
", $product_id);
$results = $wpdb->get_results($query);
$variants = array();
foreach ($results as $row) {
$attributes = array();
if ($row->attribute_string) {
$pairs = explode('|', $row->attribute_string);
foreach ($pairs as $pair) {
list($slug, $value) = explode(':', $pair, 2);
$attributes[$slug] = $value;
}
}
$variants[] = array(
'id' => (int)$row->variant_id,
'sku' => $row->sku,
'price' => (float)$row->price,
'sale_price' => $row->sale_price ? (float)$row->sale_price : null,
'stock_quantity' => (int)$row->stock_quantity,
'stock_status' => $row->stock_status,
'image_url' => $row->image_id ? wp_get_attachment_url($row->image_id) : null,
'attributes' => $attributes
);
}
return $variants;
}
}
缓存策略优化
多属性变体接口可能面临高并发访问,合理的缓存策略至关重要。
// 变体数据缓存实现示例
class Variant_Cache_Manager {
private $cache_group = 'product_variants';
private $cache_expiration = 3600; // 1小时
public function get_variants($product_id) {
$cache_key = $this->get_cache_key($product_id);
$cached = wp_cache_get($cache_key, $this->cache_group);
if ($cached !== false) {
return $cached;
}
// 从数据库获取数据
$variants = $this->fetch_variants_from_db($product_id);
// 设置缓存
wp_cache_set($cache_key, $variants, $this->cache_group, $this->cache_expiration);
return $variants;
}
public function invalidate_variant_cache($product_id) {
$cache_key = $this->get_cache_key($product_id);
wp_cache_delete($cache_key, $this->cache_group);
// 同时清除相关产品的缓存
$this->clear_related_caches($product_id);
}
private function get_cache_key($product_id) {
return 'variants_' . $product_id . '_' . get_current_blog_id();
}
private function clear_related_caches($product_id) {
// 清除产品列表页缓存
wp_cache_delete('product_list_' . get_current_blog_id(), 'products');
// 清除分类页面缓存
$terms = wp_get_post_terms($product_id, 'product_cat', array('fields' => 'ids'));
foreach ($terms as $term_id) {
wp_cache_delete('category_products_' . $term_id, 'products');
}
}
}
// 在适当的地方触发缓存清除
add_action('save_post_product', function($post_id, $post, $update) {
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
return;
}
if ($post->post_type !== 'product') {
return;
}
$cache_manager = new Variant_Cache_Manager();
$cache_manager->invalidate_variant_cache($post_id);
}, 10, 3);
设计要点四:确保数据一致性与完整性
数据库事务处理
在多属性变体系统中,创建或更新变体通常涉及多个数据库操作,使用事务可以确保数据的一致性。
// 使用数据库事务创建变体
class Variant_Manager {
public function create_variant($product_id, $variant_data) {
global $wpdb;
// 验证输入数据
$validation_result = $this->validate_variant_data($variant_data);
if (is_wp_error($validation_result)) {
return $validation_result;
}
// 开始事务
$wpdb->query('START TRANSACTION');
try {
// 插入变体基本信息
$variant_insert = $wpdb->insert(
$wpdb->prefix . 'product_variants',
array(
'product_id' => $product_id,
'sku' => $variant_data['sku'],
'price' => $variant_data['price'],
'sale_price' => isset($variant_data['sale_price']) ? $variant_data['sale_price'] : null,
'stock_quantity' => $variant_data['stock_quantity'],
'stock_status' => $variant_data['stock_status'],
'menu_order' => isset($variant_data['menu_order']) ? $variant_data['menu_order'] : 0,
'status' => 'publish',
'created_at' => current_time('mysql'),
'updated_at' => current_time('mysql')
),
array('%d', '%s', '%f', '%f', '%d', '%s', '%d', '%s', '%s', '%s')
);
if ($variant_insert === false) {
throw new Exception('Failed to insert variant');
}
$variant_id = $wpdb->insert_id;
// 插入变体属性关联
if (!empty($variant_data['attributes'])) {
foreach ($variant_data['attributes'] as $attribute_id => $attribute_value) {
$attribute_insert = $wpdb->insert(
$wpdb->prefix . 'variant_attributes',
array(
'variant_id' => $variant_id,
'attribute_id' => $attribute_id,
'attribute_value' => $attribute_value,
'created_at' => current_time('mysql')
),
array('%d', '%d', '%s', '%s')
);
if ($attribute_insert === false) {
throw new Exception('Failed to insert variant attribute');
}
}
}
// 处理变体图片
if (!empty($variant_data['image_id'])) {
$image_insert = $wpdb->insert(
$wpdb->prefix . 'variant_images',
array(
'variant_id' => $variant_id,
'image_id' => $variant_data['image_id'],
'is_primary' => 1,
'created_at' => current_time('mysql')
),
array('%d', '%d', '%d', '%s')
);
if ($image_insert === false) {
throw new Exception('Failed to insert variant image');
}
}
// 提交事务
$wpdb->query('COMMIT');
// 清除缓存
$cache_manager = new Variant_Cache_Manager();
$cache_manager->invalidate_variant_cache($product_id);
return $variant_id;
} catch (Exception $e) {
// 回滚事务
$wpdb->query('ROLLBACK');
error_log('Variant creation failed: ' . $e->getMessage());
return new WP_Error(
'variant_creation_failed',
__('Failed to create variant: ', 'your-text-domain') . $e->getMessage(),
array('status' => 500)
);
}
}
private function validate_variant_data($variant_data) {
$required_fields = array('sku', 'price', 'stock_quantity', 'stock_status');
foreach ($required_fields as $field) {
if (!isset($variant_data[$field]) || empty($variant_data[$field])) {
return new WP_Error(
'missing_required_field',
sprintf(__('Missing required field: %s', 'your-text-domain'), $field),
array('status' => 400)
);
}
}
// 验证SKU唯一性
if ($this->is_sku_exists($variant_data['sku'])) {
return new WP_Error(
'duplicate_sku',
__('SKU already exists', 'your-text-domain'),
array('status' => 400)
);
}
// 验证价格有效性
if (!is_numeric($variant_data['price']) || $variant_data['price'] < 0) {
return new WP_Error(
'invalid_price',
__('Invalid price value', 'your-text-domain'),
array('status' => 400)
);
}
return true;
}
private function is_sku_exists($sku, $exclude_variant_id = 0) {
global $wpdb;
$query = $wpdb->prepare(
"SELECT COUNT(*) FROM {$wpdb->prefix}product_variants WHERE sku = %s AND variant_id != %d",
$sku,
$exclude_variant_id
);
return $wpdb->get_var($query) > 0;
}
}
### 数据完整性约束
在数据库层面设置完整性约束,防止无效数据进入系统:
-- 数据库完整性约束示例
ALTER TABLE wp_product_variants
ADD CONSTRAINT fk_product_variants_product
FOREIGN KEY (product_id)
REFERENCES wp_posts(ID)
ON DELETE CASCADE
ON UPDATE CASCADE;
ALTER TABLE wp_variant_attributes
ADD CONSTRAINT fk_variant_attributes_variant
FOREIGN KEY (variant_id)
REFERENCES wp_product_variants(variant_id)
ON DELETE CASCADE
ON UPDATE CASCADE;
ALTER TABLE wp_variant_attributes
ADD CONSTRAINT fk_variant_attributes_attribute
FOREIGN KEY (attribute_id)
REFERENCES wp_custom_attributes(attribute_id)
ON DELETE CASCADE
ON UPDATE CASCADE;
-- 添加唯一约束,防止重复的属性组合
ALTER TABLE wp_variant_attributes
ADD UNIQUE INDEX idx_unique_variant_attribute (variant_id, attribute_id);
-- 添加检查约束(如果MySQL版本支持)
-- ALTER TABLE wp_product_variants
-- ADD CONSTRAINT chk_price_positive CHECK (price >= 0);
## 设计要点五:实现可扩展的架构设计
### 插件化架构设计
将多属性变体系统设计为可插拔的模块,便于功能扩展和维护:
// 主插件类 - 遵循WordPress插件标准
class WC_Product_Variants_Extension {
private static $instance = null;
private $modules = array();
public static function get_instance() {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct() {
$this->define_constants();
$this->init_hooks();
$this->load_modules();
}
private function define_constants() {
define('WC_PVE_VERSION', '1.0.0');
define('WC_PVE_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('WC_PVE_PLUGIN_URL', plugin_dir_url(__FILE__));
}
private function init_hooks() {
// 初始化钩子
add_action('init', array($this, 'init'));
add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts'));
add_action('wp_enqueue_scripts', array($this, 'enqueue_frontend_scripts'));
// 产品类型注册
add_filter('product_type_selector', array($this, 'add_product_type'));
// REST API初始化
add_action('rest_api_init', array($this, 'register_rest_routes'));
}
private function load_modules() {
// 自动加载模块
$modules_dir = WC_PVE_PLUGIN_DIR . 'modules/';
$module_files = array(
'attribute-manager.php',
'variant-manager.php',
'inventory-manager.php',
'pricing-manager.php',
'import-export.php'
);
foreach ($module_files as $file) {
$file_path = $modules_dir . $file;
if (file_exists($file_path)) {
require_once $file_path;
$module_class = $this->get_module_class_name($file);
if (class_exists($module_class)) {
$this->modules[$module_class] = new $module_class();
}
}
}
}
private function get_module_class_name($filename) {
$name = str_replace(array('class-', '.php'), '', $filename);
$name = str_replace('-', ' ', $name);
$name = ucwords($name);
$name = str_replace(' ', '_', $name);
return $name . '_Module';
}
public function init() {
// 初始化文本域
load_plugin_textdomain('wc-product-variants', false, dirname(plugin_basename(__FILE__)) . '/languages');
// 注册自定义数据库表
$this->register_tables();
}
private function register_tables() {
global $wpdb;
$wpdb->product_variants = $wpdb->prefix . 'product_variants';
$wpdb->variant_attributes = $wpdb->prefix . 'variant_attributes';
$wpdb->custom_attributes = $wpdb->prefix . 'custom_attributes';
// 创建或更新数据库表
$this->create_tables();
}
private function create_tables() {
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
$charset_collate = $wpdb->get_charset_collate();
// 创建产品变体表
$sql = "CREATE TABLE IF NOT EXISTS {$wpdb->product_variants} (
variant_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
product_id BIGINT UNSIGNED NOT NULL,
sku VARCHAR(100) NOT NULL,
price DECIMAL(10,2) NOT NULL DEFAULT 0.00,
sale_price DECIMAL(10,2) NULL,
stock_quantity INT DEFAULT 0,
stock_status VARCHAR(20) DEFAULT 'instock',
image_id BIGINT UNSIGNED NULL,
menu_order INT DEFAULT 0,
status VARCHAR(20) DEFAULT 'publish',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (variant_id),
UNIQUE KEY sku (sku),
KEY product_id (product_id),
KEY status (status),
KEY stock_status (stock_status)
) {$charset_collate};";
dbDelta($sql);
// 创建其他表...
}
}
// 初始化插件
function wc_product_variants_extension() {
return WC_Product_Variants_Extension::get_instance();
}
// 尽早初始化,但要在plugins_loaded之后
add_action('plugins_loaded', 'wc_product_variants_extension');
### 钩子与过滤器系统
提供丰富的钩子和过滤器,允许其他开发者扩展功能:
// 钩子系统实现
class Variant_Hooks_Manager {
public function __construct() {
$this->register_core_hooks();
}
private function register_core_hooks() {
// 变体保存前的钩子
add_action('wc_variant_before_save', array($this, 'before_variant_save'), 10, 2);
// 变体保存后的钩子
add_action('wc_variant_after_save', array($this, 'after_variant_save'), 10, 2);
// 变体价格计算过滤器
add_filter('wc_variant_calculated_price', array($this, 'filter_variant_price'), 10, 3);
// 变体库存检查过滤器
add_filter('wc_variant_check_stock', array($this, 'filter_stock_check'), 10, 2);
// 变体属性显示过滤器
add_filter('wc_variant_display_attributes', array($this, 'filter_display_attributes'), 10, 2);
}
public function before_variant_save($variant_data, $variant_id) {
// 允许其他插件修改变体数据
$variant_data = apply_filters('wc_variant_pre_save_data', $variant_data, $variant_id);
// 验证数据
$validation = apply_filters('wc_variant_validation', true, $variant_data, $variant_id);
if (is_wp_error($validation)) {
return $validation;
}
return $variant_data;
}
public function after_variant_save($variant_id, $variant_data) {
// 触发其他操作
do_action('wc_variant_saved', $variant_id, $variant_data);
// 记录日志
do_action('wc_variant_save_log', $variant_id, $variant_data);
// 发送通知
do_action('wc_variant_save_notification', $variant_id, $variant_data);
}
public function filter_variant_price($price, $variant_id, $context) {
// 允许其他插件修改价格计算逻辑
$price = apply_filters('wc_variant_base_price', $price, $variant_id, $context);
// 应用折扣
$price = apply_filters('wc_variant_apply_discounts', $price, $variant_id, $context);
// 应用税费
$price = apply_filters('wc_variant_apply_taxes', $price, $variant_id, $context);
return $price;
}
public function filter_stock_check($is_in_stock, $variant_id) {
// 允许其他插件修改库存检查逻辑
$is_in_stock = apply_filters('wc_variant_stock_status', $is_in_stock, $variant_id);
// 检查预售状态
$is_in_stock = apply_filters('wc_variant_preorder_status', $is_in_stock, $variant_id);
return $is_in_stock;
}
public function filter_display_attributes($attributes, $variant_id) {
// 允许格式化属性显示
$attributes = apply_filters('wc_variant_format_attributes', $attributes, $variant_id);
// 添加自定义属性显示
$attributes = apply_filters('wc_variant_custom_attributes', $attributes, $variant_id);
return $attributes;
}
}
// 扩展点示例 - 自定义价格计算
add_filter('wc_variant_base_price', function($price, $variant_id, $context) {
// 如果是批发用户,应用批发价格
if ($context === 'wholesale' && current_user_can('wholesale_customer')) {
$wholesale_price = get_post_meta($variant_id, '_wholesale_price', true);
if ($wholesale_price) {
return $wholesale_price;
}
}
return $price;
}, 10, 3);
// 扩展点示例 - 自定义库存管理
add_filter('wc_variant_stock_status', function($is_in_stock, $variant_id) {
// 检查是否允许备货
$allow_backorder = get_post_meta($variant_id, '_allow_backorder', true);
if ($allow_backorder === 'yes') {
return true; // 允许备货,始终显示有货
}
return $is_in_stock;
}, 10, 2);
### 模块化扩展系统
设计模块化系统,便于添加新功能而不影响核心代码:
// 基础模块抽象类
abstract class WC_Variant_Module {
protected $module_id;
protected $module_name;
protected $module_description;
protected $module_version;
abstract public function init();
abstract public function register_hooks();
public function __construct() {
$this->init();
$this->register_hooks();
}
public function get_module_info() {
return array(
'id' => $this->module_id,
'name' => $this->module_name,
'description' => $this->module_description,
'version' => $this->module_version
);
}
}
// 具体模块实现 - 批量编辑模块
class Batch_Edit_Module extends WC_Variant_Module {
public function __construct() {
$this->module_id = 'batch_edit';
$this->module_name = __('Batch Edit Variants', 'wc-product-variants');
$this->module_description = __('Batch edit product variants', 'wc-product-variants');
$this->module_version = '1.0.0';
parent::__construct();
}
public function init() {
// 初始化模块
$this->register_admin_pages();
}
public function register_hooks() {
// 注册管理页面
add_action('admin_menu', array($this, 'add_admin_menu'));
// 注册AJAX处理
add_action('wp_ajax_wc_variant_batch_edit', array($this, 'handle_batch_edit'));
// 添加批量操作
add_filter('bulk_actions-edit-product', array($this, 'add_bulk_actions'));
add_filter('handle_bulk_actions-edit-product', array($this, 'handle_bulk_actions'), 10, 3);
}
private function register_admin_pages() {
// 注册管理页面
$this->admin_pages = array(
'batch-edit' => array(
'title' => __('Batch Edit Variants', 'wc-product-variants'),
'capability' => 'manage_woocommerce',
'callback' => array($this, 'render_batch_edit_page')
)
);
}
public function add_admin_menu() {
add_submenu_page(
'edit.php?post_type=product',
__('Batch Edit Variants', 'wc-product-variants'),
__('Batch Edit', 'wc-product-variants'),
'manage_woocommerce',
'wc-variant-batch-edit',
array($this, 'render_batch_edit_page')
);
}
public function render_batch_edit_page() {
// 渲染批量编辑页面
include WC_PVE_PLUGIN_DIR . 'templates/admin/batch-edit.php';
}
public function handle_batch_edit() {
// 处理批量编辑请求
check_ajax_referer('wc_variant_batch_edit', 'nonce');
if (!current_user_can('manage_woocommerce')) {
wp_die(-1);
}
$action = $_POST['action_type'] ?? '';
$variant_ids = $_POST['variant_ids'] ?? array();
$update_data = $_POST['update_data'] ?? array();
switch ($action) {
case 'update_prices':
$result = $this->batch_update_prices($variant_ids, $update_data);
break;
case 'update_stock':
$result = $this->batch_update_stock($variant_ids, $update_data);
break;
case 'update_attributes':
$result = $this->batch_update_attributes($variant_ids, $update_data);
break;
default:
$result = new WP_Error('invalid_action', __('Invalid action', 'wc-product-variants'));
}
if (is_wp_error($result)) {
wp_send_json_error($result->get_error_message());
} else {
wp_send_json_success(array(
'message' => __('Batch update completed successfully', 'wc-product-variants'),
'updated_count' => count($variant_ids)
));
}
}
private function batch_update_prices($variant_ids, $update_data) {
global $wpdb;
$updates = array();
$formats = array();
if (isset($update_data['price'])) {
$updates['price'] = floatval($update_data['price']);
$formats[] = '%f';
}
if (isset($update_data['sale_price'])) {
$updates['sale_price'] = floatval($update_data['sale_price']);
$formats[] = '%f';
