mcpField
W
woocommerce-plugin-development

woocommerce-plugin-development

github.com/finsilabs开发6094 次安装5 天前更新
代码仓库

关于 woocommerce-plugin-development

面向 WooCommerce 插件开发者的技能,涵盖插件架构及兼顾升级的实现方式,涉及商店功能、API 和订单存储。

主要功能

  1. 01插件架构与基础模板
  2. 02订单和产品生命周期钩子
  3. 03WooCommerce 设置页面
  4. 04REST API 路由扩展
  5. 05兼容 HPOS 的订单数据访问

适用场景

  1. 01添加自定义配送、支付或折扣逻辑
  2. 02创建管理设置和配置页面
  3. 03扩展结账、订单或产品行为
  4. 04迁移插件以兼容 HPOS

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/finsilabs/awesome-ecommerce-skills --skill woocommerce-plugin-development

安装后在 Claude Code 中输入/woocommerce-plugin-development确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

tile.json
evals/custom-shipping-method-and-rest-api-endp/
criteria.json
task.md
evals/plugin-boilerplate-hpos-compatibility-or/
criteria.json
task.md
evals/woocommerce-settings-api-and-product-cus/
criteria.json
task.md

详细介绍

WooCommerce 插件开发

概述

使用 WordPress 钩子(操作和筛选器)、WooCommerce Settings API、自定义文章类型和元框、REST API 扩展以及 HPOS(高性能订单存储)兼容性,构建可扩展商店功能的自定义 WooCommerce 插件。本技能涵盖插件架构、WooCommerce 订单和产品生命周期钩子,以及构建易于维护、升级安全的扩展的模式。

使用本技能的场景

  • 构建扩展 WooCommerce 的自定义功能(自定义配送、支付或折扣逻辑)时
  • 创建可添加管理设置和配置页面的插件时
  • 接入 WooCommerce 结账、订单或产品生命周期时
  • 使用自定义端点扩展 WooCommerce REST API 时
  • 迁移插件以支持 HPOS(高性能订单存储)时

核心说明

  1. 搭建插件基础结构

    <?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();
    });
    
  2. 使用 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
            ));
        }
    }
    
  3. 使用 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',
                ],
            ];
        }
    }
    
  4. 使用筛选器修改产品数据

    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);
        }
    }
    
  5. 扩展 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,
            ]);
        }
    }
    
  6. 创建自定义配送方式

    // 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