WordPress插件开发:多站点商品数据自动导入配置实现问询
Hey there! Let's break this down step by step—building repeatable field groups in a WordPress plugin settings page using classes is totally doable, and it'll keep your code clean and maintainable. Here's how I'd approach it:
First, create a main plugin class to encapsulate all settings logic. This will handle registering the admin menu, setting up the options page, and managing the repeatable fields.
class Product_Importer_Plugin { public function __construct() { // Hook into admin menu setup add_action('admin_menu', array($this, 'add_settings_page')); // Register settings add_action('admin_init', array($this, 'register_settings')); // Enqueue admin scripts for repeatable fields add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts')); } public function add_settings_page() { add_options_page( 'Product Importer Settings', 'Product Importer', 'manage_options', 'product-importer-settings', array($this, 'render_settings_page') ); } public function register_settings() { register_setting( 'product_importer_options_group', // Option group 'product_importer_sites', // Option name (stores array of sites) array($this, 'sanitize_site_settings') // Sanitization callback ); add_settings_section( 'product_importer_main_section', 'Site Configurations', array($this, 'render_section_description'), 'product-importer-settings' ); add_settings_field( 'repeatable_sites', 'Import Sources', array($this, 'render_repeatable_fields'), 'product-importer-settings', 'product_importer_main_section' ); } // Sanitize the saved site data public function sanitize_site_settings($input) { $sanitized = array(); if (isset($input['sites'])) { foreach ($input['sites'] as $site) { // Sanitize each field $sanitized_site = array( 'name' => sanitize_text_field($site['name']), 'curl_url' => esc_url_raw($site['curl_url']), 'product_ids' => sanitize_text_field($site['product_ids']) ); // Only add if at least name and URL are present if (!empty($sanitized_site['name']) && !empty($sanitized_site['curl_url'])) { $sanitized[] = $sanitized_site; } } } return $sanitized; } public function render_section_description() { echo '<p>Add and configure sites from which you want to import products.</p>'; } public function render_settings_page() { if (!current_user_can('manage_options')) { wp_die(__('You do not have sufficient permissions to access this page.')); } ?> <div class="wrap"> <h1><?php echo esc_html(get_admin_page_title()); ?></h1> <form method="post" action="options.php"> <?php settings_fields('product_importer_options_group'); do_settings_sections('product-importer-settings'); submit_button(); ?> </form> </div> <?php } } // Initialize the plugin new Product_Importer_Plugin();
Add the method to render the repeatable fields—this will output existing saved sites and a button to add new groups.
public function render_repeatable_fields() { $sites = get_option('product_importer_sites', array()); ?> <div id="repeatable-sites-container"> <?php if (!empty($sites)): ?> <?php foreach ($sites as $index => $site): ?> <div class="site-field-group"> <h4>Site #<?php echo $index + 1; ?></h4> <p> <label for="site-name-<?php echo $index; ?>">Site Name:</label> <input type="text" id="site-name-<?php echo $index; ?>" name="product_importer_sites[sites][<?php echo $index; ?>][name]" value="<?php echo esc_attr($site['name']); ?>" class="regular-text"> </p> <p> <label for="site-curl-url-<?php echo $index; ?>">cURL Link Format (use {product_id} as placeholder):</label> <input type="text" id="site-curl-url-<?php echo $index; ?>" name="product_importer_sites[sites][<?php echo $index; ?>][curl_url]" value="<?php echo esc_attr($site['curl_url']); ?>" class="regular-text"> <small>Example: https://example.com/api/product/{product_id}</small> </p> <p> <label for="site-product-ids-<?php echo $index; ?>">Product IDs (comma-separated):</label> <input type="text" id="site-product-ids-<?php echo $index; ?>" name="product_importer_sites[sites][<?php echo $index; ?>][product_ids]" value="<?php echo esc_attr($site['product_ids']); ?>" class="regular-text"> </p> <button type="button" class="remove-site-group button">Remove Site</button> <hr> </div> <?php endforeach; ?> <?php endif; ?> </div> <button type="button" id="add-new-site-group" class="button button-primary">Add New Site</button> <?php }
You'll need a small JS snippet to handle adding/removing field groups dynamically. First, enqueue the script in your class:
public function enqueue_admin_scripts($hook) { // Only load on our settings page if ($hook !== 'settings_page_product-importer-settings') { return; } wp_enqueue_script( 'product-importer-admin-js', plugin_dir_url(__FILE__) . 'js/admin-scripts.js', array('jquery'), '1.0', true ); }
Then create js/admin-scripts.js with this code:
jQuery(document).ready(function($) { // Add new site group $('#add-new-site-group').on('click', function() { var container = $('#repeatable-sites-container'); var index = container.children('.site-field-group').length; var newGroup = ` <div class="site-field-group"> <h4>Site #${index + 1}</h4> <p> <label for="site-name-${index}">Site Name:</label> <input type="text" id="site-name-${index}" name="product_importer_sites[sites][${index}][name]" class="regular-text"> </p> <p> <label for="site-curl-url-${index}">cURL Link Format (use {product_id} as placeholder):</label> <input type="text" id="site-curl-url-${index}" name="product_importer_sites[sites][${index}][curl_url]" class="regular-text"> <small>Example: https://example.com/api/product/{product_id}</small> </p> <p> <label for="site-product-ids-${index}">Product IDs (comma-separated):</label> <input type="text" id="site-product-ids-${index}" name="product_importer_sites[sites][${index}][product_ids]" class="regular-text"> </p> <button type="button" class="remove-site-group button">Remove Site</button> <hr> </div> `; container.append(newGroup); }); // Remove site group $(document).on('click', '.remove-site-group', function() { $(this).closest('.site-field-group').remove(); // Reindex groups after removal (optional but cleaner) $('.site-field-group').each(function(index) { $(this).find('h4').text(`Site #${index + 1}`); $(this).find('input').each(function() { var name = $(this).attr('name').replace(/\[sites\]\[\d+\]/, `[sites][${index}]`); var id = $(this).attr('id').replace(/-(\d+)-/, `-${index}-`); $(this).attr('name', name).attr('id', id); }); }); }); });
Once your settings are saved, you can extend the class to handle data fetching:
- Add a method like
fetch_product_data()that loops through the saved$sitesarray - For each site, split the comma-separated product IDs and replace
{product_id}in the cURL URL with each ID - Use WordPress's
wp_remote_get()(a safer, WP-compatible wrapper for cURL) to fetch data - Sanitize the response, then create/update WordPress products with
wp_insert_post()(or WooCommerce functions if you're using WC)
Quick Tips:
- Always add error handling for failed requests—check
wp_remote_retrieve_response_code()to confirm a 200 OK status - Consider adding a cron job with
wp_schedule_event()to automate imports on a schedule - Validate the cURL URL format to ensure the
{product_id}placeholder is present before saving
内容的提问来源于stack exchange,提问作者Martijn

