关于 woocommerce-plugin-development
面向 WooCommerce 插件开发者的技能,涵盖插件架构及兼顾升级的实现方式,涉及商店功能、API 和订单存储。
主要功能
- 01插件架构与基础模板
- 02订单和产品生命周期钩子
- 03WooCommerce 设置页面
- 04REST API 路由扩展
- 05兼容 HPOS 的订单数据访问
适用场景
- 01添加自定义配送、支付或折扣逻辑
- 02创建管理设置和配置页面
- 03扩展结账、订单或产品行为
- 04迁移插件以兼容 HPOS
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ finsilabs/ awesome-ecommerce-skills --skill woocommerce-plugin-development安装后在 Claude Code 中输入/woocommerce-plugin-development确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
tile.jsonevals/custom-shipping-method-and-rest-api-endp/criteria.jsontask.mdevals/plugin-boilerplate-hpos-compatibility-or/criteria.jsontask.mdevals/woocommerce-settings-api-and-product-cus/criteria.jsontask.md详细介绍
WooCommerce 插件开发
概述
使用 WordPress 钩子(操作和筛选器)、WooCommerce Settings API、自定义文章类型和元框、REST API 扩展以及 HPOS(高性能订单存储)兼容性,构建可扩展商店功能的自定义 WooCommerce 插件。本技能涵盖插件架构、WooCommerce 订单和产品生命周期钩子,以及构建易于维护、升级安全的扩展的模式。
使用本技能的场景
- 构建扩展 WooCommerce 的自定义功能(自定义配送、支付或折扣逻辑)时
- 创建可添加管理设置和配置页面的插件时
- 接入 WooCommerce 结账、订单或产品生命周期时
- 使用自定义端点扩展 WooCommerce REST API 时
- 迁移插件以支持 HPOS(高性能订单存储)时
核心说明
-
搭建插件基础结构
<?php /** * Plugin Name: My Custom WooCommerce Extension * Description: Adds custom functionality to WooCommerce * Version: 1.0.0 * Author: Your Name * Requires Plugins: woocommerce * WC requires at least: 8.0 * WC tested up to: 9.0 * * @package MyWooExtension */ defined('ABSPATH') || exit; // Check if WooCommerce is active if (!in_array('woocommerce/woocommerce.php', apply_filters('active_plugins', get_option('active_plugins')))) { return; } define('MWE_VERSION', '1.0.0'); define('MWE_PLUGIN_DIR', plugin_dir_path(__FILE__)); define('MWE_PLUGIN_URL', plugin_dir_url(__FILE__)); // Declare HPOS compatibility add_action('before_woocommerce_init', function () { if (class_exists(\Automattic\WooCommerce\Utilities\FeaturesUtil::class)) { \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', __FILE__, true ); } }); // Initialize the plugin after WooCommerce loads add_action('woocommerce_loaded', function () { require_once MWE_PLUGIN_DIR . 'includes/class-mwe-main.php'; MWE_Main::instance(); }); -
使用 WooCommerce 钩子处理订单生命周期事件
class MWE_Order_Handler { public function __construct() { // Order status transitions add_action('woocommerce_order_status_completed', [$this, 'on_order_completed'], 10, 2); add_action('woocommerce_order_status_changed', [$this, 'on_status_change'], 10, 4); // New order created add_action('woocommerce_checkout_order_created', [$this, 'on_order_created']); // Payment complete add_action('woocommerce_payment_complete', [$this, 'on_payment_complete']); // Before/after order item saved (HPOS compatible) add_action('woocommerce_new_order_item', [$this, 'on_new_order_item'], 10, 3); } public function on_order_completed(int $order_id, \WC_Order $order): void { // Example: trigger fulfillment via external API $items = $order->get_items(); $shipping_address = $order->get_address('shipping'); foreach ($items as $item) { $product = $item->get_product(); $sku = $product->get_sku(); $quantity = $item->get_quantity(); // Call your external fulfillment service $this->send_to_fulfillment($sku, $quantity, $shipping_address); } $order->add_order_note('Order sent to fulfillment service.'); } public function on_status_change(int $order_id, string $from, string $to, \WC_Order $order): void { // Log status transitions error_log(sprintf( 'Order #%d transitioned from %s to %s', $order_id, $from, $to )); } } -
使用 WooCommerce Settings API 添加设置
class MWE_Settings { public function __construct() { add_filter('woocommerce_settings_tabs_array', [$this, 'add_settings_tab'], 50); add_action('woocommerce_settings_tabs_mwe_settings', [$this, 'render_settings']); add_action('woocommerce_update_options_mwe_settings', [$this, 'save_settings']); } public function add_settings_tab(array $tabs): array { $tabs['mwe_settings'] = __('My Extension', 'my-woo-extension'); return $tabs; } public function render_settings(): void { woocommerce_admin_fields($this->get_settings()); } public function save_settings(): void { woocommerce_update_options($this->get_settings()); } private function get_settings(): array { return [ [ 'title' => __('General Settings', 'my-woo-extension'), 'type' => 'title', 'id' => 'mwe_general_settings', ], [ 'title' => __('Enable Feature', 'my-woo-extension'), 'desc' => __('Enable the custom feature', 'my-woo-extension'), 'id' => 'mwe_enable_feature', 'type' => 'checkbox', 'default' => 'yes', ], [ 'title' => __('API Key', 'my-woo-extension'), 'desc' => __('Enter your external service API key', 'my-woo-extension'), 'id' => 'mwe_api_key', 'type' => 'text', 'css' => 'min-width: 300px;', ], [ 'title' => __('Processing Mode', 'my-woo-extension'), 'id' => 'mwe_processing_mode', 'type' => 'select', 'options' => [ 'sync' => __('Synchronous', 'my-woo-extension'), 'async' => __('Asynchronous (Queue)', 'my-woo-extension'), ], 'default' => 'sync', ], [ 'type' => 'sectionend', 'id' => 'mwe_general_settings', ], ]; } } -
使用筛选器修改产品数据
class MWE_Product_Customizer { public function __construct() { // Add a custom product tab in admin add_filter('woocommerce_product_data_tabs', [$this, 'add_product_tab']); add_action('woocommerce_product_data_panels', [$this, 'render_product_panel']); add_action('woocommerce_process_product_meta', [$this, 'save_product_meta']); // Modify price display on frontend add_filter('woocommerce_get_price_html', [$this, 'modify_price_display'], 10, 2); // Add custom data to cart item add_filter('woocommerce_add_cart_item_data', [$this, 'add_custom_cart_data'], 10, 3); // Display custom data in cart add_filter('woocommerce_get_item_data', [$this, 'display_cart_item_data'], 10, 2); // Save custom data to order item add_action('woocommerce_checkout_create_order_line_item', [$this, 'save_order_item_meta'], 10, 4); } public function add_product_tab(array $tabs): array { $tabs['mwe_custom'] = [ 'label' => __('Custom Fields', 'my-woo-extension'), 'target' => 'mwe_custom_product_data', 'class' => [], 'priority' => 80, ]; return $tabs; } public function render_product_panel(): void { global $post; echo '<div id="mwe_custom_product_data" class="panel woocommerce_options_panel">'; wp_nonce_field('mwe_save_product_meta', 'mwe_product_meta_nonce'); woocommerce_wp_text_input([ 'id' => '_mwe_custom_field', 'label' => __('Custom Field', 'my-woo-extension'), 'description' => __('Enter a custom value for this product', 'my-woo-extension'), 'desc_tip' => true, ]); woocommerce_wp_select([ 'id' => '_mwe_product_badge', 'label' => __('Product Badge', 'my-woo-extension'), 'options' => [ '' => __('None', 'my-woo-extension'), 'new' => __('New', 'my-woo-extension'), 'sale' => __('Sale', 'my-woo-extension'), 'limited' => __('Limited Edition', 'my-woo-extension'), ], ]); echo '</div>'; } public function save_product_meta(int $post_id): void { // Verify nonce before processing $_POST data if (!isset($_POST['mwe_product_meta_nonce']) || !wp_verify_nonce($_POST['mwe_product_meta_nonce'], 'mwe_save_product_meta')) { return; } $custom_field = sanitize_text_field($_POST['_mwe_custom_field'] ?? ''); update_post_meta($post_id, '_mwe_custom_field', $custom_field); $badge = sanitize_text_field($_POST['_mwe_product_badge'] ?? ''); update_post_meta($post_id, '_mwe_product_badge', $badge); } } -
扩展 WooCommerce REST API
class MWE_REST_Controller { public function __construct() { add_action('rest_api_init', [$this, 'register_routes']); } public function register_routes(): void { register_rest_route('mwe/v1', '/analytics/summary', [ 'methods' => \WP_REST_Server::READABLE, 'callback' => [$this, 'get_analytics_summary'], 'permission_callback' => [$this, 'check_admin_permission'], ]); register_rest_route('mwe/v1', '/products/(?P<id>\d+)/custom-data', [ 'methods' => \WP_REST_Server::READABLE, 'callback' => [$this, 'get_product_custom_data'], 'permission_callback' => '__return_true', 'args' => [ 'id' => [ 'validate_callback' => function ($param) { return is_numeric($param); }, ], ], ]); } public function check_admin_permission(\WP_REST_Request $request): bool { return current_user_can('manage_woocommerce'); } public function get_analytics_summary(\WP_REST_Request $request): \WP_REST_Response { $days = absint($request->get_param('days') ?? 30); $date_from = date('Y-m-d', strtotime("-{$days} days")); // HPOS-compatible order query $orders = wc_get_orders([ 'date_created' => ">={$date_from}", 'status' => ['wc-completed', 'wc-processing'], 'limit' => -1, 'return' => 'ids', ]); $total_revenue = 0; foreach ($orders as $order_id) { $order = wc_get_order($order_id); $total_revenue += (float) $order->get_total(); } return new \WP_REST_Response([ 'period' => "{$days}_days", 'total_orders' => count($orders), 'total_revenue' => $total_revenue, 'avg_order' => count($orders) > 0 ? $total_revenue / count($orders) : 0, ]); } } -
创建自定义配送方式
// Register the shipping method add_filter('woocommerce_shipping_methods', function (array $methods): array { $methods['mwe_custom_shipping'] = 'MWE_Custom_Shipping_Method'; return $methods; }); class MWE_Custom_Shipping_Method extends \WC_Shipping_Method { public function __construct(int $instance_id = 0) { $this->id = 'mwe_custom_shipping'; $this->instance_id = absint($instance_id); $this->method_title = __('Custom Shipping', 'my-woo-extension'); $this->method_description = __('Custom shipping rate calculation', 'my-woo-extension'); $this->supports = ['shipping-zones', 'instance-settings']; $this->init(); } public function init(): void { $this->init_form_fields(); $this->init_settings(); $this->title = $this->get_option('title', 'Custom Shipping'); $this->enabled = $this->get_option('enabled', 'yes'); } public function init_form_fields(): void { $this->instance_form_fields = [ 'title' => [ 'title' => __('Method Title', 'my-woo-extension'), 'type' => 'text', 'default' => 'Custom Shipping', ], 'base_cost' => [ 'title' => __('Base Cost', 'my-woo-extension'), 'type' => 'price', 'default' => '5.00', ], 'per_item_cost' => [ 'title' => __('Per Item Cost', 'my-woo-extension'), 'type' => 'price', 'default' => '1.00', ], ]; } public function calculate_shipping($package = []): void { $base_cost = (float) $this->get_option('base_cost', 5); $per_item = (float) $this->get_option('per_item_cost', 1); $item_count = 0; foreach ($package['contents'] as $item) { $item_count += $item['quantity']; } $cost = $base_cost + ($per_item * $item_count); $this->add_rate([ 'id' => $this->get_rate_id(), 'label' => $this->title, 'cost' => $cost, ]); } }
示例
自定义 WooCommerce 邮件通知
class MWE_Custom_Email extends \WC_Email {
public function __construct() {
$this->id = 'mwe_order_shipped';
$this->title = __('Order Shipped', 'my-woo-extension');
$this->description = __('Sent when an order is marked as shipped', 'my-woo-extension');
$this->heading = __('Your order has shipped!', 'my-woo-extension');
$this->subject = __('Your {site_title} order #{order_number} has shipped', 'my-woo-extension');
$this->template_base = MWE_PLUGIN_DIR . 'templates/';
$this->template_html = 'emails/order-shipped.php';
$this->template_plain = 'emails/plain/order-shipped.php';
$this->customer_email = true;
add_action('mwe_order_shipped_notification', [$this, 'trigger'], 10, 2);
parent::__construct();
}
public function trigger(int $order_id, string $tracking_number): void {
$this->object = wc_get_order($order_id);
if (!$this->object) return;
$this->recipient = $this->object->get_billing_email();
$this->placeholders['{tracking_number}'] = $tracking_number;
if ($this->is_enabled() && $this->get_recipient()) {
$this->send(
$this->get_recipient(),
$this->get_subject(),
$this->get_content(),
$this->get_headers(),
$this->get_attachments()
);
}
}
}
// Register the email class
add_filter('woocommerce_email_classes', function (array $emails): array {
$emails['MWE_Order_Shipped'] = new MWE_Custom_Email();
return $emails;
});
兼容 HPOS 的订单元数据访问
// Old way (post meta) — DEPRECATED
// $value = get_post_meta($order_id, '_custom_field', true);
// New way (HPOS compatible)
$order = wc_get_order($order_id);
// Read custom meta
$custom_value = $order->get_meta('_mwe_custom_field', true);
// Write custom meta
$order->update_meta_data('_mwe_custom_field', 'new_value');
$order->save();
// Query orders with custom meta (HPOS compatible)
$orders = wc_get_orders([
'meta_query' => [
[
'key' => '_mwe_custom_field',
'value' => 'specific_value',
],
],
'status' => 'completed',
'limit' => 50,
]);
最佳实践
- 始终声明 HPOS 兼容性 — WooCommerce 正在迁移到自定义订单表;使用
FeaturesUtil::declare_compatibility,避免直接查询订单的wp_posts - 使用 WooCommerce CRUD 方法 — 通过
$order->get_total()访问订单或产品数据,而不是使用get_post_meta();CRUD 方法同时适用于传统存储和 HPOS 存储 - 选择合适的钩子优先级 — 默认优先级为 10;使用较低的数值(5)可在 WooCommerce 自身的处理程序之前运行,使用较高的数值(20 及以上)则在之后运行
- 对所有内容进行清理和转义 — 对输入使用
sanitize_text_field(),对输出使用esc_html()/esc_attr();切勿信任$_POST或$_GET数据 - 使用 WooCommerce 内置函数 —
wc_get_orders()、wc_get_products()、wc_price()可处理边缘情况,并能保持向前兼容 - 为元数据键添加命名空间 — 为所有自定义元数据添加插件标识作为前缀(例如
_mwe_),以避免与其他插件冲突 - 支持多站点 — 如果插件需要兼容 WordPress 多站点,请使用
is_plugin_active_for_network()检查 - 使用 WooCommerce 测试套件 — 以
WC_Unit_Test_Case作为 PHPUnit 测试的基类
常见问题
| 问题 | 解决方案 |
|---|---|
| WooCommerce 更新后插件失效 | 接入 woocommerce_loaded,而不是 plugins_loaded,以确保 WooCommerce 类已加载 |
| 启用 HPOS 后订单元数据无法保存 | 使用 $order->update_meta_data() 和 $order->save(),而不是 update_post_meta() |
| 钩子多次触发(重复发送邮件、重复扣减库存) | 使用静态标记或暂存值检查钩子是否已处理;也可使用 did_action() 检查 |
| 自定义配送方式未显示 | 确保通过 woocommerce_shipping_methods 筛选器注册该方式,且类继承自 WC_Shipping_Method |
| REST API 端点返回 403 | 检查 permission_callback — 管理端点使用 current_user_can('manage_woocommerce'),公开端点使用 '__return_true' |
相关技能
- @woocommerce-performance
- @product-data-modeling
- @discount-engine
- @ecommerce-seo
- @erp-integration
评论
支持 Markdown