WooCommerce 产品过滤器

一款功能强大且用户友好的 WooCommerce 产品过滤解决方案。

<?php
/**
* Plugin Name: BDFG Product Filters for WooCommerce
* Plugin URI: https://beiduofengou.net/2024/11/16/bdfg-product-filters-for-woocommerce/
* Description: A powerful and user-friendly product filtering solution for WooCommerce. Developed by Beiduofengou team.
* Version: 2.5.0
* Author: beiduofengou
* Author URI: https://beiduofengou.net
* Text Domain: bdfg-product-filters
* Domain Path: /languages
* Requires at least: 5.8
* Requires PHP: 7.4
* WC requires at least: 5.0
* WC tested up to: 8.0
* License: GPL v2 or later
*
* @package BDFG_Product_Filters
* @author beiduofengou <[email protected]>
* @link https://beiduofengou.net
* @since 1.0.0
*/

// 防止直接访问此文件
if (!defined('ABSPATH')) {
exit('Direct access not allowed.');
}

if (!class_exists('BDFG_Product_Filters')) {

/**
* 主插件类
*
* @since 2.0.0
*/
class BDFG_Product_Filters {

/**
* 插件实例
*
* @var BDFG_Product_Filters
*/
private static $instance = null;

/**
* 插件版本号
*
* @var string
*/
private $version = '2.5.0';

/**
* 错误信息数组
*
* @var array
*/
private $errors = array();

/**
* 获取插件实例
*
* @return BDFG_Product_Filters
*/
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();

// 加载翻译文件
add_action('plugins_loaded', array($this, 'load_plugin_textdomain'));

// 检查依赖
if ($this->check_requirements()) {
$this->includes();
$this->init();

// 添加插件设置链接
add_filter('plugin_action_links_' . plugin_basename(__FILE__), array($this, 'add_plugin_links'));
}
}

/**
* 定义插件常量
*/
private function define_constants() {
define('BDFG_VERSION', $this->version);
define('BDFG_FILE', __FILE__);
define('BDFG_PATH', plugin_dir_path(__FILE__));
define('BDFG_URL', plugin_dir_url(__FILE__));
define('BDFG_BASENAME', plugin_basename(__FILE__));
define('BDFG_ASSETS_URL', BDFG_URL . 'assets/');
}

/**
* 初始化钩子
*/
private function init_hooks() {
// 注册激活和停用钩子
register_activation_hook(__FILE__, array($this, 'activate'));
register_deactivation_hook(__FILE__, array($this, 'deactivate'));

// 后台钩子
if (is_admin()) {
add_action('admin_menu', array($this, 'add_admin_menu'));
add_action('admin_enqueue_scripts', array($this, 'admin_scripts'));
add_action('admin_notices', array($this, 'show_admin_notices'));
}

// 前台钩子
add_action('wp_enqueue_scripts', array($this, 'frontend_scripts'));
add_action('init', array($this, 'register_shortcodes'));
}

/**
* 检查插件依赖
*
* @return bool
*/
public function check_requirements() {
if (!class_exists('WooCommerce')) {
$this->errors[] = sprintf(
__('%s requires WooCommerce to be installed and activated. You can download %s here.', 'bdfg-product-filters'),
'<strong>BDFG Product Filters</strong>',
'<a href="https://wordpress.org/plugins/woocommerce/" target="_blank">WooCommerce</a>'
);
add_action('admin_notices', array($this, 'show_admin_notices'));
return false;
}

if (version_compare(PHP_VERSION, '7.4', '<')) {
$this->errors[] = sprintf(
__('%s requires PHP version 7.4 or higher. Please upgrade PHP to run this plugin.', 'bdfg-product-filters'),
'<strong>BDFG Product Filters</strong>'
);
add_action('admin_notices', array($this, 'show_admin_notices'));
return false;
}

return true;
}

/**
* 显示管理员通知
*/
public function show_admin_notices() {
if (!empty($this->errors)) {
foreach ($this->errors as $error) {
echo '<div class="error"><p>' . wp_kses_post($error) . '</p></div>';
}
}
}

/**
* 包含必要的文件
*/
private function includes() {
// 工具类
require_once BDFG_PATH . 'includes/class-bdfg-helper.php';

// 核心类
require_once BDFG_PATH . 'includes/class-bdfg-core-filter.php';
require_once BDFG_PATH . 'includes/class-bdfg-advanced-features.php';

// 管理员类
if (is_admin()) {
require_once BDFG_PATH . 'admin/class-bdfg-admin.php';
require_once BDFG_PATH . 'admin/class-bdfg-settings.php';
}

// API类
require_once BDFG_PATH . 'includes/api/class-bdfg-api.php';
}

/**
* 初始化插件
*/
private function init() {
// 初始化过滤器类型
new BDFG_Taxonomy_Filter();
new BDFG_Price_Filter();
new BDFG_Review_Filter();
new BDFG_Name_Filter();

// 添加短代码支持
add_shortcode('bdfg_filter', array($this, 'render_filter_shortcode'));

// 初始化高级功能
if (get_option('bdfg_enable_advanced_features', 'yes') === 'yes') {
BDFG_Init_Advanced_Features();
}
}

/**
* 前端脚本和样式
*/
public function frontend_scripts() {
// 注册样式
wp_register_style(
'bdfg-filters-style',
BDFG_ASSETS_URL . 'css/frontend.min.css',
array(),
BDFG_VERSION
);

// 注册脚本
wp_register_script(
'bdfg-filters-script',
BDFG_ASSETS_URL . 'js/frontend.min.js',
array('jquery', 'wp-util'),
BDFG_VERSION,
true
);

// 本地化脚本
wp_localize_script('bdfg-filters-script', 'bdfgFilters', array(
'ajaxurl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('bdfg-filters-nonce'),
'i18n' => array(
'noResults' => __('No products found', 'bdfg-product-filters'),
'loading' => __('Loading...', 'bdfg-product-filters'),
'error' => __('Error occurred', 'bdfg-product-filters')
)
));

// 在商店和分类页面加载资源
if (is_shop() || is_product_category() || is_product_tag()) {
wp_enqueue_style('bdfg-filters-style');
wp_enqueue_script('bdfg-filters-script');
}
}

/**
* 渲染过滤器短代码
*
* @param array $atts 短代码属性
* @return string
*/
public function render_filter_shortcode($atts) {
$atts = shortcode_atts(array(
'type' => 'taxonomy',
'taxonomy' => 'product_cat',
'title' => '',
'display' => 'dropdown',
'hide_empty' => 'yes',
'show_count' => 'yes',
'orderby' => 'name',
'order' => 'ASC'
), $atts, 'bdfg_filter');

// 开始输出缓冲
ob_start();

// 加载对应的模板
$template_path = BDFG_PATH . 'templates/' . $atts['type'] . '-filter.php';
$theme_template_path = get_stylesheet_directory() . '/bdfg-product-filters/' . $atts['type'] . '-filter.php';

// 检查主题是否有自定义模板
if (file_exists($theme_template_path)) {
$template_path = $theme_template_path;
}

if (file_exists($template_path)) {
include $template_path;
} else {
_e('Template not found.', 'bdfg-product-filters');
}

return ob_get_clean();
}

/**
* 激活插件
*/
public function activate() {
// 创建必要的数据库表和选项
$this->create_tables();
$this->add_default_options();

// 清除缓存
$this->clear_cache();

// 刷新重写规则
flush_rewrite_rules();

// 记录激活时间
update_option('bdfg_activated_time', time());
update_option('bdfg_version', BDFG_VERSION);
}

/**
* 创建数据库表
*/
private function create_tables() {
global $wpdb;

$charset_collate = $wpdb->get_charset_collate();

// 创建缓存表
$sql = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}bdfg_cache (
cache_key varchar(255) NOT NULL,
cache_value longtext NOT NULL,
expiration bigint(20) NOT NULL,
PRIMARY KEY (cache_key)
) $charset_collate;";

require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql);
}

/**
* 添加默认选项
*/
private function add_default_options() {
$default_options = array(
'bdfg_enable_advanced_features' => 'yes',
'bdfg_cache_duration' => 3600,
'bdfg_show_vendor_info' => 'yes',
'bdfg_enable_recommendations' => 'yes',
'bdfg_products_per_page' => 12
);

foreach ($default_options as $key => $value) {
if (get_option($key) === false) {
update_option($key, $value);
}
}
}

/**
* 停用插件
*/
public function deactivate() {
// 清除所有缓存
$this->clear_cache();

// 记录停用时间
update_option('bdfg_deactivated_time', time());
}

/**
* 清除缓存
*/
private function clear_cache() {
global $wpdb;

// 清除瞬态
$wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '%_transient_bdfg_%'");
$wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '%_transient_timeout_bdfg_%'");

// 清除缓存表
$wpdb->query("TRUNCATE TABLE {$wpdb->prefix}bdfg_cache");
}

/**
* 加载插件文本域
*/
public function load_plugin_textdomain() {
load_plugin_textdomain(
'bdfg-product-filters',
false,
dirname(plugin_basename(__FILE__)) . '/languages/'
);
}

/**
* 添加插件设置链接
*
* @param array $links 现有的插件链接
* @return array 修改后的插件链接
*/
public function add_plugin_links($links) {
$plugin_links = array(
'<a href="' . admin_url('admin.php?page=bdfg-settings') . '">' . __('Settings', 'bdfg-product-filters') . '</a>',
'<a href="https://beiduofengou.net/docs/bdfg-product-filters/" target="_blank">' . __('Documentation', 'bdfg-product-filters') . '</a>',
'<a href="https://beiduofengou.net/support/" target="_blank">' . __('Support', 'bdfg-product-filters') . '</a>'
);
return array_merge($plugin_links, $links);
}
}

/**
* 返回主插件类的实例
*
* @return BDFG_Product_Filters
*/
function BDFG_Product_Filters() {
return BDFG_Product_Filters::get_instance();
}

// 初始化插件
BDFG_Product_Filters();
}

class-bdfg-core-filter.php

相关文章: WordPress 智能缓存管理

<?php
/**
* BDFG Product Filters - Core Filter Classes
*
* @package BDFG_Product_Filters
* @author beiduofengou
* @link https://beiduofengou.net
* @since 2.0.0
*/

if (!defined('ABSPATH')) { exit('Direct access not allowed.');
}

/**
* 过滤器基类
*
* @since 2.0.0
*/
abstract class BDFG_Filter_Base {
/**
* 过滤器类型
* @var string
*/
protected $filter_type;

/**
* 过滤器选项
* @var array
*/
protected $filter_options;

/**
* 缓存时间(秒)
* @var int
*/
protected $cache_time;

/**
* 构造函数
*/
public function __construct() {
// 从设置获取缓存时间,默认1小时
$this->cache_time = (int)get_option('bdfg_cache_duration', 3600);

$this->init();
$this->add_hooks();

// 添加调试日志
if(defined('WP_DEBUG') && WP_DEBUG) {
error_log(sprintf('[BDFG] Initialized filter: %s', $this->filter_type));
}
}

/**
* 初始化过滤器
*/
protected function init() {
// 获取过滤器特定选项
$this->filter_options = get_option(
'bdfg_filter_' . $this->filter_type . '_options',
$this->get_default_options()
);
}

/**
* 获取默认选项
*
* @return array
*/
protected function get_default_options() {
return array();
}

/**
* 添加WordPress钩子
*/
protected function add_hooks() {
// 修改产品查询
add_filter('woocommerce_product_query', array($this, 'modify_product_query'), 10, 2);

// AJAX处理
add_action('wp_ajax_bdfg_filter_products', array($this, 'ajax_filter_products'));
add_action('wp_ajax_nopriv_bdfg_filter_products', array($this, 'ajax_filter_products'));

// 添加自定义REST API端点
add_action('rest_api_init', array($this, 'register_rest_routes'));
}

/**
* 生成缓存键
*
* @param array $args 查询参数
* @return string
*/
protected function get_cache_key($args) {
return 'bdfg_' . $this->filter_type . '_' . md5(serialize($args));
}

/**
* 获取缓存结果
*
* @param string $key 缓存键
* @return mixed|false
*/
protected function get_cached_results($key) {
global $wpdb;

$now = time();

$result = $wpdb->get_row($wpdb->prepare(
"SELECT cache_value FROM {$wpdb->prefix}bdfg_cache
WHERE cache_key = %s AND expiration > %d",
$key,
$now
));

return $result ? maybe_unserialize($result->cache_value) : false;
}

/**
* 设置缓存结果
*
* @param string $key 缓存键
* @param mixed $data 要缓存的数据
*/
protected function set_cached_results($key, $data) {
global $wpdb;

$expiration = time() + $this->cache_time;

$wpdb->replace(
$wpdb->prefix . 'bdfg_cache',
array(
'cache_key' => $key,
'cache_value' => maybe_serialize($data),
'expiration' => $expiration
),
array('%s', '%s', '%d')
);
}

/**
* 注册REST API路由
*/
public function register_rest_routes() {
register_rest_route('bdfg/v1', '/' . $this->filter_type . '/filter', array(
'methods' => 'GET',
'callback' => array($this, 'rest_filter_products'),
'permission_callback' => '__return_true'
));
}

/**
* REST API过滤处理
*
* @param WP_REST_Request $request
* @return WP_REST_Response
*/
public function rest_filter_products($request) {
try {
$params = $request->get_params();
$results = $this->filter_products($params);

return rest_ensure_response(array(
'success' => true,
'data' => $results
));

} catch (Exception $e) {
return new WP_Error(
'bdfg_filter_error',
$e->getMessage(),
array('status' => 400)
);
}
}

/**
* 格式化产品数据
*
* @param WC_Product $product
* @return array
*/
protected function format_product_data($product) {
return array(
'id' => $product->get_id(),
'name' => $product->get_name(),
'price_html' => $product->get_price_html(),
'thumbnail' => get_the_post_thumbnail_url($product->get_id(), 'woocommerce_thumbnail'),
'permalink' => get_permalink($product->get_id()),
'average_rating' => $product->get_average_rating(),
'review_count' => $product->get_review_count(),
'stock_status' => $product->get_stock_status(),
'categories' => wp_list_pluck(
get_the_terms($product->get_id(), 'product_cat'),
'name'
),
'tags' => wp_list_pluck(
get_the_terms($product->get_id(), 'product_tag'),
'name'
),
);
}

/**
* 记录错误日志
*
* @param string $message
* @param mixed $context
*/
protected function log_error($message, $context = array()) {
if (defined('WP_DEBUG') && WP_DEBUG) {
error_log(sprintf(
'[BDFG Error][%s] %s | Context: %s',
$this->filter_type,
$message,
print_r($context, true)
));
}
}

/**
* 渲染过滤器HTML
*
* @param array $args 过滤器参数
* @return string 过滤器HTML
*/
abstract public function render_filter($args = array());

/**
* 修改WooCommerce产品查询
*
* @param WP_Query $query
* @param array $query_vars
* @return WP_Query
*/
abstract public function modify_product_query($query, $query_vars);

/**
* AJAX产品过滤处理
*/
abstract public function ajax_filter_products();
}

/**
* 注册核心过滤器
*/
function bdfg_register_core_filters() {
$filters = array(
'BDFG_Taxonomy_Filter',
'BDFG_Price_Filter',
'BDFG_Review_Filter',
'BDFG_Name_Filter'
);

foreach ($filters as $filter_class) {
if (class_exists($filter_class)) {
new $filter_class();
}
}
}
add_action('init', 'bdfg_register_core_filters');

/**
* 分类过滤器实现类
*/
class BDFG_Taxonomy_Filter extends BDFG_Filter_Base {
/**
* 构造函数
*/
public function __construct() {
$this->filter_type = 'taxonomy';
parent::__construct();
}

/**
* 渲染分类过滤器
*/
public function render_filter($args = array()) {
$defaults = array(
'taxonomy' => 'product_cat',
'display_type' => 'dropdown',
'hide_empty' => true,
'show_count' => true,
'hierarchical' => true,
'orderby' => 'name',
'order' => 'ASC'
);

$args = wp_parse_args($args, $defaults);
$terms = $this->get_taxonomy_terms($args);

ob_start();
?>
<div class="bdfg-filter bdfg-taxonomy-filter" data-filter-type="taxonomy" data-taxonomy="<?php echo esc_attr($args['taxonomy']); ?>">
<?php if ($args['display_type'] === 'dropdown'): ?>
<select class="bdfg-select" name="bdfg_tax_<?php echo esc_attr($args['taxonomy']); ?>">
<option value=""><?php echo sprintf(__('Select %s', 'bdfg-product-filters'), $this->get_taxonomy_label($args['taxonomy'])); ?></option>
<?php $this->render_term_options($terms, $args); ?>
</select>
<?php else: ?>
<ul class="bdfg-checkbox-list">
<?php $this->render_term_checkboxes($terms, $args); ?>
</ul>
<?php endif; ?>
</div>
<?php
return ob_get_clean();
}

/**
* 获取分类列表
*/
protected function get_taxonomy_terms($args) {
$cache_key = $this->get_cache_key($args);
$terms = $this->get_cached_results($cache_key);

if ($terms === false) {
$terms = get_terms(array(
'taxonomy' => $args['taxonomy'],
'hide_empty' => $args['hide_empty'],
'hierarchical' => $args['hierarchical'],
'orderby' => $args['orderby'],
'order' => $args['order']
));

if (!is_wp_error($terms)) {
$this->set_cached_results($cache_key, $terms);
} else {
$this->log_error('Failed to get taxonomy terms', array(
'taxonomy' => $args['taxonomy'],
'error' => $terms->get_error_message()
));
$terms = array();
}
}

return $terms;
}

/**
* 渲染分类选项
*/
protected function render_term_options($terms, $args, $depth = 0) {
foreach ($terms as $term) {
$indent = str_repeat('&nbsp;&nbsp;&nbsp;', $depth);
printf(
'<option value="%s">%s%s%s</option>',
esc_attr($term->slug),
$indent,
esc_html($term->name),
$args['show_count'] ? ' (' . absint($term->count) . ')' : ''
);

if ($args['hierarchical'] && !empty($term->children)) {
$this->render_term_options($term->children, $args, $depth + 1);
}
}
}

/**
* 渲染分类复选框
*/
protected function render_term_checkboxes($terms, $args, $depth = 0) {
foreach ($terms as $term) {
$indent = str_repeat('&nbsp;&nbsp;&nbsp;', $depth);
?>
<li class="bdfg-checkbox-item level-<?php echo esc_attr($depth); ?>">
<label>
<input type="checkbox"
name="bdfg_tax_<?php echo esc_attr($args['taxonomy']); ?>[]"
value="<?php echo esc_attr($term->slug); ?>">
<?php
echo $indent . esc_html($term->name);
if ($args['show_count']) {
echo ' (' . absint($term->count) . ')';
}
?>
</label>
</li>
<?php
if ($args['hierarchical'] && !empty($term->children)) {
echo '<ul class="bdfg-checkbox-children">';
$this->render_term_checkboxes($term->children, $args, $depth + 1);
echo '</ul>';
}
}
}

/**
* 修改产品查询
*/
public function modify_product_query($query, $query_vars) {
if (!is_admin() && $query->is_main_query() && (is_shop() || is_product_category())) {
$tax_query = array();

foreach ($_GET as $key => $value) {
if (strpos($key, 'bdfg_tax_') === 0) {
$taxonomy = str_replace('bdfg_tax_', '', $key);
if (!empty($value)) {
$tax_query[] = array(
'taxonomy' => sanitize_key($taxonomy),
'field' => 'slug',
'terms' => is_array($value) ? array_map('sanitize_title', $value) : array(sanitize_title($value)),
'operator' => 'IN'
);
}
}
}

if (!empty($tax_query)) {
// 合并现有的tax_query
$existing_tax_query = $query->get('tax_query', array());
if (!empty($existing_tax_query)) {
$tax_query = array_merge(
array('relation' => 'AND'),
$existing_tax_query,
$tax_query
);
}
$query->set('tax_query', $tax_query);
}
}

return $query;
}

/**
* AJAX产品过滤
*/
public function ajax_filter_products() {
check_ajax_referer('bdfg-filters-nonce', 'nonce');

$taxonomies = isset($_POST['taxonomies']) ?
(array)$_POST['taxonomies'] : array();

try {
$args = array(
'post_type' => 'product',
'posts_per_page' => apply_filters('bdfg_products_per_page', 12),
'paged' => isset($_POST['page']) ? absint($_POST['page']) : 1,
'tax_query' => array('relation' => 'AND')
);

foreach ($taxonomies as $taxonomy => $terms) {
if (!empty($terms)) {
$args['tax_query'][] = array(
'taxonomy' => sanitize_key($taxonomy),
'field' => 'slug',
'terms' => array_map('sanitize_title', (array)$terms),
'operator' => 'IN'
);
}
}

$query = new WP_Query($args);
$results = array(
'products' => array(),
'pagination' => $this->get_pagination_data($query)
);

if ($query->have_posts()) {
while ($query->have_posts()) {
$query->the_post();
$product = wc_get_product(get_the_ID());
$results['products'][] = array(
'id' => $product->get_id(),
'name' => $product->get_name(),
'price_html' => $product->get_price_html(),
'thumbnail' => get_the_post_thumbnail_url($product->get_id(), 'woocommerce_thumbnail'),
'permalink' => get_permalink(),
'average_rating' => $product->get_average_rating(),
'review_count' => $product->get_review_count()
);
}
wp_reset_postdata();
}

wp_send_json_success($results);

} catch (Exception $e) {
$this->log_error('AJAX filter error', array(
'error' => $e->getMessage(),
'taxonomies' => $taxonomies
));
wp_send_json_error(array(
'message' => __('An error occurred while filtering products.', 'bdfg-product-filters')
));
}
}
}

/**
* 价格过滤器实现类
*/
class BDFG_Price_Filter extends BDFG_Filter_Base {
/**
* 构造函数
*/
public function __construct() {
$this->filter_type = 'price';
parent::__construct();
}

/**
* 渲染价格过滤器
*/
public function render_filter($args = array()) {
$defaults = array(
'min_price' => '',
'max_price' => '',
'step' => 10,
'display' => 'both'
);

$args = wp_parse_args($args, $defaults);
$price_range = $this->get_price_range();

if ($price_range) {
$args['min_price'] = $args['min_price'] ?: $price_range->min_price;
$args['max_price'] = $args['max_price'] ?: $price_range->max_price;
}

ob_start();
?>
<div class="bdfg-filter bdfg-price-filter" data-filter-type="price">
<div class="bdfg-price-slider-wrapper">
<?php if ($args['display'] !== 'input'): ?>
<div class="bdfg-price-slider" id="bdfg-price-slider"
data-min="<?php echo esc_attr($args['min_price']); ?>"
data-max="<?php echo esc_attr($args['max_price']); ?>"
data-step="<?php echo esc_attr($args['step']); ?>">
</div>
<?php endif; ?>

<?php if ($args['display'] !== 'slider'): ?>
<div class="bdfg-price-inputs">
<div class="bdfg-price-input">
<label><?php esc_html_e('Min Price', 'bdfg-product-filters'); ?></label>
<input type="number"
id="bdfg-min-price"
name="min_price"
value="<?php echo esc_attr($args['min_price']); ?>"
min="<?php echo esc_attr($args['min_price']); ?>"
max="<?php echo esc_attr($args['max_price']); ?>"
step="<?php echo esc_attr($args['step']); ?>">
</div>
<div class="bdfg-price-input">
<label><?php esc_html_e('Max Price', 'bdfg-product-filters'); ?></label>
<input type="number"
id="bdfg-max-price"
name="max_price"
value="<?php echo esc_attr($args['max_price']); ?>"
min="<?php echo esc_attr($args['min_price']); ?>"
max="<?php echo esc_attr($args['max_price']); ?>"
step="<?php echo esc_attr($args['step']); ?>">
</div>
</div>
<?php endif; ?>

<button type="button" class="bdfg-filter-button">
<?php esc_html_e('Filter', 'bdfg-product-filters'); ?>
</button>
</div>
</div>
<?php
return ob_get_clean();
}

/**
* 获取价格范围
*/
protected function get_price_range() {
global $wpdb;

$cache_key = $this->get_cache_key(array('price_range' => true));
$price_range = $this->get_cached_results($cache_key);

if ($price_range === false) {
$price_range = $wpdb->get_row("
SELECT
MIN(CAST(min_price AS DECIMAL(10,2))) as min_price,
MAX(CAST(max_price AS DECIMAL(10,2))) as max_price
FROM {$wpdb->wc_product_meta_lookup}
WHERE min_price > 0
");

if ($price_range) {
// 四舍五入到最接近的整数
$price_range->min_price = floor($price_range->min_price);
$price_range->max_price = ceil($price_range->max_price);

$this->set_cached_results($cache_key, $price_range);
}
}

return $price_range;
}

/**
* 修改产品查询
*/
public function modify_product_query($query, $query_vars) {
if (!is_admin() && $query->is_main_query() && (is_shop() || is_product_category())) {
$min_price = isset($_GET['min_price']) ? floatval($_GET['min_price']) : '';
$max_price = isset($_GET['max_price']) ? floatval($_GET['max_price']) : '';

if ($min_price !== '' || $max_price !== '') {
$meta_query = $query->get('meta_query', array());

$meta_query[] = array(
'relation' => 'AND',
array(
'key' => '_price',
'value' => array($min_price, $max_price),
'type' => 'NUMERIC',
'compare' => 'BETWEEN'
),
array(
'key' => '_price', 12),
'paged' => isset($_POST['page']) ? absint($_POST['page']) : 1,
'meta_query' => array()
);

if ($min_price !== '' || $max_price !== '') {
$args['meta_query'][] = array(
'relation' => 'AND',
array(
'key' => '_price',
'value' => array($min_price, $max_price),
'type' => 'NUMERIC',
'compare' => 'BETWEEN'
),
array(
'key' => '_price',
'value' => 0,
'type' => 'NUMERIC',
'compare' => '>'
)
);
}

$query = new WP_Query($args);
$results = array(
'products' => array(),
'pagination' => $this->get_pagination_data($query)
);

if ($query->have_posts()) {
while ($query->have_posts()) {
$query->the_post();
$product = wc_get_product(get_the_ID());
$results['products'][] = array(
'id' => $product->get_id(),
'name' => $product->get_name(),
'price_html' => $product->get_price_html(),
'thumbnail' => get_the_post_thumbnail_url($product->get_id(), 'woocommerce_thumbnail'),
'permalink' => get_permalink(),
'average_rating' => $product->get_average_rating(),
'review_count' => $product->get_review_count()
);
}
wp_reset_postdata();
}

wp_send_json_success($results);

} catch (Exception $e) {
$this->log_error('AJAX price filter error', array(
'error' => $e->getMessage(),
'min_price' => $min_price,
'max_price' => $max_price
));
wp_send_json_error(array(
'message' => __('An error occurred while filtering products by price.', 'bdfg-product-filters')
));
}
}
}

/**
* 评分过滤器实现类
*/
class BDFG_Review_Filter extends BDFG_Filter_Base {
/**
* 构造函数
*/
public function __construct() {
$this->filter_type = 'review';
parent::__construct();
}

/**
* 渲染评分过滤器
*/
public function render_filter($args = array()) {
$defaults = array(
'show_count' => true,
'min_rating' => 1,
'max_rating' => 5
);

$args = wp_parse_args($args, $defaults);

ob_start();
?>
<div class="bdfg-filter bdfg-review-filter" data-filter-type="review">
<ul class="bdfg-rating-list">
<?php for ($i = $args['max_rating']; $i >= $args['min_rating']; $i--): ?>
<li class="bdfg-rating-item">
<label>
<input type="checkbox" name="rating[]" value="<?php echo esc_attr($i); ?>">
<span class="bdfg-star-rating">
<?php
for ($j = 1; $j <= 5; $j++) {
echo '<span class="star ' . ($j <= $i ? 'filled' : '') . '">' .
($j <= $i ? '★' : '☆') . '</span>';
}
?>
</span>
<?php
if ($args['show_count']) {
$count = $this->get_rating_count($i);
echo '<span class="count">(' . absint($count) . ')</span>';
}
?>
</label>
</li>
<?php endfor; ?>
</ul>
</div>
<?php
return ob_get_clean();
}

/**
* 获取特定评分的商品数量
*/
protected function get_rating_count($rating) {
global $wpdb;

$cache_key = $this->get_cache_key(array('rating' => $rating));
$count = $this->get_cached_results($cache_key);

if ($count === false) {
$count = $wpdb->get_var($wpdb->prepare("
SELECT COUNT(*)
FROM {$wpdb->prefix}wc_product_meta_lookup
WHERE average_rating >= %f
AND average_rating < %f
", $rating - 0.5, $rating + 0.5));

$this->set_cached_results($cache_key, $count);
}

return $count;
}

/**
* 修改产品查询
*/
public function modify_product_query($query, $query_vars) {
if (!is_admin() && $query->is_main_query() && (is_shop() || is_product_category())) {
if (isset($_GET['rating']) && !empty($_GET['rating'])) {
$ratings = array_map('absint', (array)$_GET['rating']);

if (!empty($ratings)) {
$meta_query = $query->get('meta_query', array());

$rating_query = array('relation' => 'OR');
foreach ($ratings as $rating) {
$rating_query[] = array(
'key' => '_wc_average_rating',
'value' => array($rating - 0.5, $rating + 0.5),
'type' => 'DECIMAL(10,2)',
'compare' => 'BETWEEN'
);
}

$meta_query[] = $rating_query;
$query->set('meta_query', $meta_query);
}
}
}

return $query;
}

/**
* AJAX产品过滤
*/
public function ajax_filter_products() {
check_ajax_referer('bdfg-filters-nonce', 'nonce');

$ratings = isset($_POST['ratings']) ? array_map('absint', (array)$_POST['ratings']) : array();

try {
$args = array(
'post_type' => 'product',
'posts_per_page' => apply_filters('bdfg_products_per_page', 12),
'paged' => isset($_POST['page']) ? absint($_POST['page']) : 1,
'meta_query' => array()
);

if (!empty($ratings)) {
$rating_query = array('relation' => 'OR');
foreach ($ratings as $rating) {
$rating_query[] = array(
'key' => '_wc_average_rating',
'value' => array($rating - 0.5, $rating + 0.5),
'type' => 'DECIMAL(10,2)',
'compare' => 'BETWEEN'
);
}
$args['meta_query'][] = $rating_query;
}

$query = new WP_Query($args);
$results = array(
'products' => array(),
'pagination' => $this->get_pagination_data($query)
);

if ($query->have_posts()) {
while ($query->have_posts()) {
$query->the_post();
$product = wc_get_product(get_the_ID());
$results['products'][] = array(
'id' => $product->get_id(),
'name' => $product->get_name(),
'price_html' => $product->get_price_html(),
'thumbnail' => get_the_post_thumbnail_url($product->get_id(), 'woocommerce_thumbnail'),
'permalink' => get_permalink(),
'average_rating' => $product->get_average_rating(),
'review_count' => $product->get_review_count()
);
}
wp_reset_postdata();
}

wp_send_json_success($results);

} catch (Exception $e) {
$this->log_error('AJAX rating filter error', array(
'error' => $e->getMessage(),
'ratings' => $ratings
));
wp_send_json_error(array(
'message' => __('An error occurred while filtering products by rating.', 'bdfg-product-filters')
));
}
}
}

/**
* 名称搜索过滤器实现类
*/
class BDFG_Name_Filter extends BDFG_Filter_Base {
/**
* 构造函数
*/
public function __construct() {
$this->filter_type = 'name';
parent::__construct();
}

/**
* 渲染搜索过滤器
*/
public function render_filter($args = array()) {
$defaults = array(
'placeholder' => __('Search products...', 'bdfg-product-filters'),
'min_chars' => 3,
'show_button' => true
);

$args = wp_parse_args($args, $defaults);

ob_start();
?>
<div class="bdfg-filter bdfg-name-filter" data-filter-type="name">
<div class="bdfg-search-wrapper">
<input type="text"
class="bdfg-search-input"
name="bdfg_search"
placeholder="<?php echo esc_attr($args['placeholder']); ?>"
data-min-chars="<?php echo esc_attr($args['min_chars']); ?>"
value="<?php echo esc_attr(isset($_GET['bdfg_search']) ? $_GET['bdfg_search'] : ''); ?>">

<?php if ($args['show_button']): ?>
<button type="button" class="bdfg-search-button">
<span class="screen-reader-text"><?php esc_html_e('Search', 'bdfg-product-filters'); ?></span>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="11" cy="11" r="8"></circle>
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
</svg>
</button>
<?php endif; ?>
</div>
<div class="bdfg-search-results" style="display: none;"></div>
</div>
<?php
return ob_get_clean();
}

/**
* 修改产品查询
*/
public function modify_product_query($query, $query_vars) {
if (!is_admin() && $query->is_main_query() && (is_shop() || is_product_category())) {
if (isset($_GET['bdfg_search']) && !empty($_GET['bdfg_search'])) {
$search_term = sanitize_text_field($_GET['bdfg_search']);

$query->set('s', $search_term);
$query->set('post_type', 'product');

// 添加SKU搜索支持
add_filter('posts_search', array($this, 'extend_product_search'), 10, 2);
}
}

return $query;
}

/**
* 扩展产品搜索以包含SKU
*/
public function extend_product_search($where, $query) {
global $wpdb;

if (!empty($_GET['bdfg_search']) && $query->is_main_query()) {
$search_term = sanitize_text_field($_GET['bdfg_search']);

$where .= $wpdb->prepare(" OR {$wpdb->posts}.ID IN (
SELECT post_id FROM {$wpdb->postmeta}
WHERE meta_key = '_sku'
AND meta_value LIKE %s
)", '%' . $wpdb->esc_like($search_term) . '%');
}

return $where;
}

/**
* AJAX产品搜索
*/
public function ajax_filter_products() {
check_ajax_referer('bdfg-filters-nonce', 'nonce');

$search_term = isset($_POST['search']) ? sanitize_text_field($_POST['search']) : '';
$min_chars = isset($_POST['min_chars']) ? absint($_POST['min_chars']) : 3;

try {
if (empty($search_term) || strlen($search_term) < $min_chars) {
wp_send_json_error(array(
'message' => sprintf(
__('Please enter at least %d characters', 'bdfg-product-filters'),
$min_chars
)
));
return;
}

$args = array(
'post_type' => 'product',
'posts_per_page' => apply_filters('bdfg_products_per_page', 12),
'paged' => isset($_POST['page']) ? absint($_POST['page']) : 1,
's' => $search_term,
'orderby' => 'relevance',
'meta_query' => array(
'relation' => 'OR',
array(
'key' => '_sku',
'value' => $search_term,
'compare' => 'LIKE'
)
)
);

$query = new WP_Query($args);
$results = array(
'products' => array(),
'pagination' => $this->get_pagination_data($query)
);

if ($query->have_posts()) {
while ($query->have_posts()) {
$query->the_post();
$product = wc_get_product(get_the_ID());
$results['products'][] = array(
'id' => $product->get_id(),
'name' => $product->get_name(),
'price_html' => $product->get_price_html(),
'thumbnail' => get_the_post_thumbnail_url($product->get_id(), 'woocommerce_thumbnail'),
'permalink' => get_permalink(),
'sku' => $product->get_sku(),
'average_rating' => $product->get_average_rating(),
'review_count' => $product->get_review_count()
);
}
wp_reset_postdata();
}

wp_send_json_success($results);

} catch (Exception $e) {
$this->log_error('AJAX search filter error', array('error' => $e->getMessage(),
'search_term' => $search_term,
'date' => '2025-02-16 10:06:12',
'user' => 'beiduofengou'
));
wp_send_json_error(array(
'message' => __('An error occurred while searching products.', 'bdfg-product-filters')
));
}
}
}

/**
* 分页数据辅助函数
*
* @param WP_Query $query
* @return array
*/
function get_pagination_data($query) {
$current_page = max(1, $query->get('paged'));
$total_pages = max(1, $query->max_num_pages);

$pagination = array(
'current_page' => $current_page,
'total_pages' => $total_pages,
'total_items' => $query->found_posts,
'items_per_page' => $query->get('posts_per_page'),
'links' => array()
);

// 生成分页链接
if ($total_pages > 1) {
// 首页
if ($current_page > 1) {
$pagination['links'][] = array(
'page' => 1,
'text' => '&laquo;',
'class' => 'first'
);
}

// 上一页
if ($current_page > 1) {
$pagination['links'][] = array(
'page' => $current_page - 1,
'text' => '&lsaquo;',
'class' => 'prev'
);
}

// 页码链接
$range = 2;
$showitems = ($range * 2) + 1;

for ($i = 1; $i <= $total_pages; $i++) {
if (
$i == 1 ||
$i == $total_pages ||
($i >= $current_page - $range && $i <= $current_page + $range)
) {
$pagination['links'][] = array(
'page' => $i,
'text' => (string)$i,
'class' => $i == $current_page ? 'current' : 'number'
);
} elseif (
$i == $current_page - $range - 1 ||
$i == $current_page + $range + 1
) {
$pagination['links'][] = array(
'page' => null,
'text' => '...',
'class' => 'dots'
);
}
}

// 下一页
if ($current_page < $total_pages) {
$pagination['links'][] = array(
'page' => $current_page + 1,
'text' => '&rsaquo;',
'class' => 'next'
);
}

// 末页
if ($current_page < $total_pages) {
$pagination['links'][] = array(
'page' => $total_pages,
'text' => '&raquo;',
'class' => 'last'
);
}
}

return $pagination;
}

/**
* 获取分类标签
*
* @param string $taxonomy
* @return string
*/
function get_taxonomy_label($taxonomy) {
$tax_obj = get_taxonomy($taxonomy);
return $tax_obj ? $tax_obj->labels->singular_name : '';
}

/**
* 记录调试信息到日志文件
*
* @param string $message
* @param array $context
*/
function bdfg_log($message, $context = array()) {
if (defined('WP_DEBUG') && WP_DEBUG) {
$log_dir = WP_CONTENT_DIR . '/bdfg-logs';
if (!file_exists($log_dir)) {
wp_mkdir_p($log_dir);
}

$date = current_time('Y-m-d');
$log_file = $log_dir . '/debug-' . $date . '.log';

$log_message = sprintf(
"[%s] %s | Context: %s\n",
current_time('Y-m-d H:i:s'),
$message,
json_encode($context, JSON_UNESCAPED_UNICODE)
);

error_log($log_message, 3, $log_file);
}
}

// 注册过滤器实例
add_action('init', 'bdfg_register_core_filters');

/**
* 获取过滤器实例
*
* @param string $type
* @return BDFG_Filter_Base|null
*/
function bdfg_get_filter($type) {
global $bdfg_filters;

if (!isset($bdfg_filters)) {
$bdfg_filters = array();
}

if (!isset($bdfg_filters[$type])) {
$class_name = 'BDFG_' . ucfirst($type) . '_Filter';
if (class_exists($class_name)) {
$bdfg_filters[$type] = new $class_name();
}
}

return isset($bdfg_filters[$type]) ? $bdfg_filters[$type] : null;
}

/**
* 渲染过滤器HTML
*
* @param string $type
* @param array $args
* @return string
*/
function bdfg_render_filter($type, $args = array()) {
$filter = bdfg_get_filter($type);
if ($filter) {
return $filter->render_filter($args);
}
return '';
}

/**
* 检查过滤器是否可用
*
* @param string $type
* @return bool
*/
function bdfg_is_filter_available($type) {
return bdfg_get_filter($type) !== null;
}

// 加载过滤器模板
function bdfg_get_template($template_name, $args = array()) {
$template_path = BDFG_PATH . 'templates/' . $template_name;
$theme_template_path = get_stylesheet_directory() . '/bdfg-product-filters/' . $template_name;

// 检查主题是否有自定义模板
if (file_exists($theme_template_path)) {
$template_path = $theme_template_path;
}

if (file_exists($template_path)) {
extract($args);
include $template_path;
}
}

class-bdfg-advanced-features.php

<?php
/**
* BDFG Product Filters - Advanced Features
*
* 提供高级过滤、供应商功能和智能推荐
*
* @package BDFG_Product_Filters
* @author Bei Duo Feng Ou <[email protected]>
* @version 2.5.0
* @update 2024-11-16 10:15:02
* @author beiduofengou
*/

if (!defined('ABSPATH')) {
exit('Direct access not allowed.');
}

/**
* 高级过滤器管理类
*/
class BDFG_Advanced_Filter_Manager {
/**
* 初始化钩子
*/
public function __construct() {
// AJAX处理
add_action('wp_ajax_bdfg_advanced_filter', array($this, 'handle_advanced_filter'));
add_action('wp_ajax_nopriv_bdfg_advanced_filter', array($this, 'handle_advanced_filter'));

// REST API支持
add_action('rest_api_init', array($this, 'register_rest_routes'));

// 添加高级过滤器小部件
add_action('widgets_init', array($this, 'register_widgets'));

// 过滤器增强
add_filter('bdfg_filter_options', array($this, 'enhance_filter_options'), 10, 2);
}

/**
* 处理高级过滤
*/
public function handle_advanced_filter() {
check_ajax_referer('bdfg-filters', 'nonce');

try {
// 获取并验证请求参数
$params = $this->validate_request_params($_POST);

// 应用过滤器
$results = $this->apply_advanced_filters($params);

// 发送成功响应
wp_send_json_success(array(
'products' => $results['products'],
'pagination' => $this->get_pagination_data($results),
'facets' => $this->get_active_facets($params)
));

} catch (Exception $e) {
$this->log_error('Advanced filter error: ' . $e->getMessage());
wp_send_json_error(array(
'message' => $e->getMessage()
));
}
}

/**
* 验证请求参数
*
* @param array $params
* @return array
*/
private function validate_request_params($params) {
return array(
'attributes' => isset($params['attributes']) ?
$this->sanitize_attributes($params['attributes']) : array(),
'price_range' => isset($params['price_range']) ?
$this->sanitize_price_range($params['price_range']) : array(),
'stock_status' => isset($params['stock_status']) ?
sanitize_text_field($params['stock_status']) : '',
'vendors' => isset($params['vendors']) ?
array_map('absint', (array)$params['vendors']) : array(),
'sort_by' => isset($params['sort_by']) ?
sanitize_text_field($params['sort_by']) : 'menu_order',
'page' => isset($params['page']) ? absint($params['page']) : 1,
'per_page' => $this->get_products_per_page()
);
}

/**
* 应用高级过滤器
*
* @param array $params 过滤参数
* @return array
*/
private function apply_advanced_filters($params) {
// 基础查询参数
$args = array(
'post_type' => 'product',
'posts_per_page' => $params['per_page'],
'paged' => $params['page'],
'tax_query' => array('relation' => 'AND'),
'meta_query' => array('relation' => 'AND')
);

// 处理属性过滤
if (!empty($params['attributes'])) {
foreach ($params['attributes'] as $taxonomy => $terms) {
if (!empty($terms)) {
$args['tax_query'][] = array(
'taxonomy' => sanitize_key($taxonomy),
'field' => 'term_id',
'terms' => array_map('absint', $terms),
'operator' => 'IN'
);
}
}
}

// 处理价格范围
if (!empty($params['price_range'])) {
$args['meta_query'][] = array(
'key' => '_price',
'value' => array(
floatval($params['price_range']['min']),
floatval($params['price_range']['max'])
),
'type' => 'NUMERIC',
'compare' => 'BETWEEN'
);
}

// 处理库存状态
if (!empty($params['stock_status'])) {
$args['meta_query'][] = array(
'key' => '_stock_status',
'value' => $params['stock_status'],
'compare' => '='
);
}

// 处理供应商过滤
if (!empty($params['vendors'])) {
$args['tax_query'][] = array(
'taxonomy' => 'product_vendor',
'field' => 'term_id',
'terms' => $params['vendors'],
'operator' => 'IN'
);
}

// 处理排序
$this->apply_sorting($args, $params['sort_by']);

// 执行查询
$query = new WP_Query($args);

return array(
'products' => $this->format_products($query->posts),
'total' => $query->found_posts,
'max_pages' => $query->max_num_pages
);
}

/**
* 应用排序规则
*
* @param array $args 查询参数
* @param string $sort_by 排序类型
*/
private function apply_sorting(&$args, $sort_by) {
switch ($sort_by) {
case 'price_low':
$args['orderby'] = 'meta_value_num';
$args['meta_key'] = '_price';
$args['order'] = 'ASC';
break;

case 'price_high':
$args['orderby'] = 'meta_value_num';
$args['meta_key'] = '_price';
$args['order'] = 'DESC';
break;

case 'rating':
$args['orderby'] = 'meta_value_num';
$args['meta_key'] = '_wc_average_rating';
$args['order'] = 'DESC';
break;

case 'popularity':
$args['orderby'] = 'meta_value_num';
$args['meta_key'] = 'total_sales';
$args['order'] = 'DESC';
break;

case 'date':
$args['orderby'] = 'date';
$args['order'] = 'DESC';
break;

default: // menu_order
$args['orderby'] = 'menu_order title';
$args['order'] = 'ASC';
break;
}
}

/**
* 格式化商品数据
*
* @param array $posts WP_Post对象数组
* @return array
*/
private function format_products($posts) {
$formatted = array();

foreach ($posts as $post) {
$product = wc_get_product($post);
if (!$product) continue;

$formatted[] = array(
'id' => $product->get_id(),
'name' => $product->get_name(),
'permalink' => $product->get_permalink(),
'price_html' => $product->get_price_html(),
'regular_price' => $product->get_regular_price(),
'sale_price' => $product->get_sale_price(),
'thumbnail' => get_the_post_thumbnail_url($product->get_id(), 'woocommerce_thumbnail'),
'average_rating' => $product->get_average_rating(),
'review_count' => $product->get_review_count(),
'stock_status' => $product->get_stock_status(),
'stock_quantity' => $product->get_stock_quantity(),
'sku' => $product->get_sku(),
'categories' => $this->get_product_terms($product, 'product_cat'),
'tags' => $this->get_product_terms($product, 'product_tag'),
'attributes' => $this->get_product_attributes($product),
'vendor' => $this->get_product_vendor($product->get_id()),
'is_on_sale' => $product->is_on_sale(),
'is_featured' => $product->is_featured(),
'updated_at' => '2024-11-16 10:15:02',
'updated_by' => 'beiduofengou'
);
}

return $formatted;
}

/**
* 获取商品分类术语
*
* @param WC_Product $product
* @param string $taxonomy
* @return array
*/
private function get_product_terms($product, $taxonomy) {
$terms = get_the_terms($product->get_id(), $taxonomy);
if (!$terms || is_wp_error($terms)) return array();

return array_map(function($term) {
return array(
'id' => $term->term_id,
'name' => $term->name,
'slug' => $term->slug,
'permalink' => get_term_link($term)
);
}, $terms);
}

/**
* 获取商品属性
*
* @param WC_Product $product
* @return array
*/
private function get_product_attributes($product) {
$attributes = array();

if ($product->is_type('variable')) {
$attrs = $product->get_variation_attributes();
} else {
$attrs = $product->get_attributes();
}

foreach ($attrs as $attr => $value) {
$taxonomy = wc_attribute_taxonomy_name(str_replace('pa_', '', $attr));

if (taxonomy_exists($taxonomy)) {
$terms = wc_get_product_terms($product->get_id(), $taxonomy);
$attributes[$attr] = array_map(function($term) {
return array(
'id' => $term->term_id,
'name' => $term->name,
'slug' => $term->slug
);
}, $terms);
} else {
$attributes[$attr] = is_array($value) ? $value : array($value);
}
}

return $attributes;
}

/**
* 获取商品供应商信息
*
* @param int $product_id
* @return array|null
*/
private function get_product_vendor($product_id) {
$vendors = get_the_terms($product_id, 'product_vendor');

if ($vendors && !is_wp_error($vendors)) {
$vendor = reset($vendors);
return array(
'id' => $vendor->term_id,
'name' => $vendor->name,
'slug' => $vendor->slug,
'permalink' => get_term_link($vendor),
'description' => $vendor->description,
'meta' => get_term_meta($vendor->term_id)
);
}

return null;
}

/**
* 获取活动的过滤项
*
* @param array $params
* @return array
*/
private function get_active_facets($params) {
$facets = array();

// 处理属性过滤
if (!empty($params['attributes'])) {
foreach ($params['attributes'] as $taxonomy => $terms) {
$tax_obj = get_taxonomy($taxonomy);
if (!$tax_obj) continue;

$term_names = array();
foreach ($terms as $term_id) {
$term = get_term($term_id, $taxonomy);
if ($term && !is_wp_error($term)) {
$term_names[] = $term->name;
}
}

if (!empty($term_names)) {
$facets[] = array(
'type' => 'attribute',
'taxonomy' => $taxonomy,
'label' => $tax_obj->labels->singular_name,
'values' => $term_names
);
}
}
}

// 处理价格范围
if (!empty($params['price_range'])) {
$facets[] = array(
'type' => 'price',
'label' => __('Price', 'bdfg-product-filters'),
'min' => $params['price_range']['min'],
'max' => $params['price_range']['max']
);
}

// 处理库存状态
if (!empty($params['stock_status'])) {
$status_labels = array(
'instock' => __('In stock', 'bdfg-product-filters'),
'outofstock' => __('Out of stock', 'bdfg-product-filters'),
'onbackorder' => __('On backorder', 'bdfg-product-filters')
);

$facets[] = array(
'type' => 'stock',
'label' => __('Stock status', 'bdfg-product-filters'),
'value' => isset($status_labels[$params['stock_status']]) ?
$status_labels[$params['stock_status']] :
$params['stock_status']
);
}

// 处理供应商
if (!empty($params['vendors'])) {
$vendor_names = array();
foreach ($params['vendors'] as $vendor_id) {
$vendor = get_term($vendor_id, 'product_vendor');
if ($vendor && !is_wp_error($vendor)) {
$vendor_names[] = $vendor->name;
}
}

if (!empty($vendor_names)) {
$facets[] = array(
'type' => 'vendor',
'label' => __('Vendor', 'bdfg-product-filters'),
'values' => $vendor_names
);
}
}

return $facets;
}

/**
* 清理属性数据
*
* @param array $attributes
* @return array
*/
private function sanitize_attributes($attributes) {
$clean = array();

foreach ($attributes as $taxonomy => $terms) {
if (taxonomy_exists($taxonomy)) {
$clean[sanitize_key($taxonomy)] = array_map('absint', (array)$terms);
}
}

return $clean;
}

/**
* 清理价格范围数据
*
* @param array $price_range
* @return array
*/
private function sanitize_price_range($price_range) {
return array(
'min' => isset($price_range['min']) ? floatval($price_range['min']) : '',
'max' => isset($price_range['max']) ? floatval($price_range['max']) : ''
);
}

/**
* 获取分页数据
*
* @param array $results
* @return array
*/
private function get_pagination_data($results) {
return array(
'total_items' => $results['total'],
'total_pages' => $results['max_pages'],
'per_page' => $this->get_products_per_page()
);
}

/**
* 注册REST API路由
*/
public function register_rest_routes() {
register_rest_route('bdfg/v1', '/filter', array(
array(
'methods' => 'GET',
'callback' => array($this, 'handle_rest_filter'),
'permission_callback' => '__return_true',
'args' => array(
'attributes' => array(
'type' => 'object',
'default' => array()
),
'price_range' => array(
'type' => 'object',
'default' => array()
),
'stock_status' => array(
'type' => 'string',
'default' => ''
),
'vendors' => array(
'type' => 'array',
'default' => array()
),
'sort_by' => array(
'type' => 'string',
'default' => 'menu_order'
),
'page' => array(
'type' => 'integer',
'default' => 1,
'minimum' => 1
)
)
)
));
}

/**
* 处理REST API过滤请求
*
* @param WP_REST_Request $request
* @return WP_REST_Response
*/
public function handle_rest_filter($request) {
try {
$params = $this->validate_request_params($request->get_params());
$results = $this->apply_advanced_filters($params);

return new WP_REST_Response(array(
'products' => $results['products'],
'pagination' => $this->get_pagination_data($results),
'facets' => $this->get_active_facets($params)
), 200);

} catch (Exception $e) {
$this->log_error('REST API filter error: ' . $e->getMessage());
return new WP_REST_Response(array(
'message' => $e->getMessage()
), 400);
}
}

/**
* 注册小部件
*/
public function register_widgets() {
register_widget('BDFG_Advanced_Filter_Widget');
}

/**
* 增强过滤器选项
*
* @param array $options
* @param string $filter_type
* @return array
*/
public function enhance_filter_options($options, $filter_type) {
switch ($filter_type) {
case 'attribute':
$options['display_type'] = array(
'dropdown' => __('Dropdown', 'bdfg-product-filters'),
'list' => __('List', 'bdfg-product-filters'),
'color' => __('Color swatches', 'bdfg-product-filters'),
'image' => __('Image swatches', 'bdfg-product-filters')
);
break;

case 'price':
$options['price_range_mode'] = array(
'slider' => __('Slider', 'bdfg-product-filters'),
'input' => __('Input fields', 'bdfg-product-filters'),
'list' => __('Predefined ranges', 'bdfg-product-filters')
);
break;

case 'rating':
$options['display_style'] = array(
'star' => __('Star rating', 'bdfg-product-filters'),
'number' => __('Number rating', 'bdfg-product-filters')
);
break;
}

return $options;
}

/**
* 生成推荐商品
*
* @param int $product_id 商品ID
* @param int $user_id 用户ID
* @return array
*/
private function generate_recommendations($product_id, $user_id) {
try {
$cache_key = 'bdfg_recommendations_' . $product_id . '_' . $user_id;
$cached_results = $this->get_cached_results($cache_key);

if ($cached_results !== false) {
return $cached_results;
}

$recommendations = array(
'similar_products' => $this->get_similar_products($product_id),
'frequently_bought' => $this->get_frequently_bought_together($product_id),
'personalized' => $this->get_personalized_recommendations($user_id)
);

// 缓存结果
$this->set_cached_results($cache_key, $recommendations);

return $recommendations;

} catch (Exception $e) {
$this->log_error('Failed to generate recommendations', array(
'product_id' => $product_id,
'user_id' => $user_id,
'error' => $e->getMessage(),
'date' => '2024-11-16 10:16:39',
'user' => 'beiduofengou'
));

return array(
'similar_products' => array(),
'frequently_bought' => array(),
'personalized' => array()
);
}
}

/**
* 获取缓存的结果
*
* @param string $key
* @return mixed
*/
private function get_cached_results($key) {
return get_transient($key);
}

/**
* 设置缓存结果
*
* @param string $key
* @param mixed $value
*/
private function set_cached_results($key, $value) {
set_transient($key, $value, $this->get_cache_duration());
}

/**
* 获取缓存时间
*
* @return int
*/
private function get_cache_duration() {
return absint(get_option('bdfg_cache_duration', 3600));
}

/**
* 获取每页显示的商品数量
*
* @return int
*/
private function get_products_per_page() {
return absint(get_option('bdfg_products_per_page', 12));
}

/**
* 记录错误日志
*
* @param string $message
* @param array $context
*/
private function log_error($message, $context = array()) {
if (defined('WP_DEBUG') && WP_DEBUG) {
error_log(sprintf(
'[BDFG Error][%s] %s | Context: %s',
current_time('mysql'),
$message,
json_encode($context, JSON_UNESCAPED_UNICODE)
));
}
}
}

// 初始化高级功能管理器
add_action('plugins_loaded', function() {
new BDFG_Advanced_Filter_Manager();
});

assets/js/frontend.js

相关文章: WooCommerce 客户分析报告插件

/**
* BDFG Product Filters - Frontend JavaScript
*
* @package BDFG_Product_Filters
* @author Bei Duo Feng Ou <[email protected]>
* @version 2.5.0
*/

(function($) {
'use strict';

// 全局变量
var BDFG = {
filters: {},
timeout: null,
isLoading: false
};

/**
* 初始化过滤器
*/
BDFG.init = function() {
// 初始化各类过滤器
this.initTaxonomyFilter();
this.initPriceFilter();
this.initReviewFilter();
this.initSearchFilter();

// 初始化高级功能
if (bdfgFilters.advanced_enabled) {
this.initAdvancedFeatures();
}

// 绑定事件
this.bindEvents();

// 从URL恢复过滤状态
this.restoreFiltersFromUrl();
};

/**
* 初始化分类过滤器
*/
BDFG.initTaxonomyFilter = function() {
$('.bdfg-taxonomy-filter select, .bdfg-taxonomy-filter input[type="checkbox"]').on('change', function() {
BDFG.updateFilters();
});
};

/**
* 初始化价格过滤器
*/
BDFG.initPriceFilter = function() {
var $slider = $('#bdfg-price-slider');

if ($slider.length) {
var minPrice = parseFloat($slider.data('min')),
maxPrice = parseFloat($slider.data('max')),
currentMin = parseFloat($('#bdfg-min-price').val()),
currentMax = parseFloat($('#bdfg-max-price').val());

$slider.slider({
range: true,
min: minPrice,
max: maxPrice,
values: [currentMin || minPrice, currentMax || maxPrice],
slide: function(event, ui) {
$('#bdfg-min-price').val(ui.values[0]);
$('#bdfg-max-price').val(ui.values[1]);
},
stop: function() {
BDFG.updateFilters();
}
});

// 手动输入价格
$('.bdfg-price-input input').on('change', function() {
var min = parseFloat($('#bdfg-min-price').val()),
max = parseFloat($('#bdfg-max-price').val());

$slider.slider('values', [min, max]);
BDFG.updateFilters();
});
}
};

/**
* 初始化评分过滤器
*/
BDFG.initReviewFilter = function() {
$('.bdfg-rating-filter input').on('change', function() {
BDFG.updateFilters();
});
};

/**
* 初始化搜索过滤器
*/
BDFG.initSearchFilter = function() {
var $input = $('.bdfg-search-input'),
minChars = parseInt($input.data('min-chars')) || 3;

$input.on('keyup', function() {
clearTimeout(BDFG.timeout);

var term = $(this).val();
if (term.length >= minChars || term.length === 0) {
BDFG.timeout = setTimeout(function() {
BDFG.updateFilters();
}, 500);
}
});
};

/**
* 初始化高级功能
*/
BDFG.initAdvancedFeatures = function() {
// 初始化供应商过滤器
this.initVendorFilter();

// 初始化智能推荐
if ($('.bdfg-recommendations').length) {
this.loadRecommendations();
}
};

/**
* 初始化供应商过滤器
*/
BDFG.initVendorFilter = function() {
$('.bdfg-vendor-filter input').on('change', function() {
BDFG.updateFilters();
});
};

/**
* 绑定事件
*/
BDFG.bindEvents = function() {
// 清除所有过滤器
$('.bdfg-clear-filters').on('click', function(e) {
e.preventDefault();
BDFG.clearAllFilters();
});

// 移除单个过滤标签
$(document).on('click', '.bdfg-filter-tag .remove', function() {
var $tag = $(this).closest('.bdfg-filter-tag'),
type = $tag.data('type'),
value = $tag.data('value');

BDFG.removeFilter(type, value);
});

// 排序变更
$('.bdfg-sorting').on('change', function() {
BDFG.updateFilters();
});

// 分页点击
$(document).on('click', '.bdfg-pagination a', function(e) {
e.preventDefault();
var page = $(this).data('page');
BDFG.goToPage(page);
});
};

/**
* 更新过滤器
*/
BDFG.updateFilters = function() {
if (BDFG.isLoading) {
return;
}

BDFG.isLoading = true;
$('.bdfg-products-wrapper').addClass('bdfg-loading');

// 收集过滤条件
var filters = BDFG.collectFilters();

// 更新URL
BDFG.updateUrl(filters);

// 发送AJAX请求
$.ajax({
url: bdfgFilters.ajaxurl,
type: 'POST',
data: {
action: 'bdfg_filter_products',
nonce: bdfgFilters.nonce,
filters: filters
},
success: function(response) {
if (response.success) {
BDFG.updateProductsGrid(response.data);
BDFG.updateActiveFilters(filters);
BDFG.updatePagination(response.data.pagination);
} else {
console.error('BDFG Filter Error:', response.data.message);
}
},
error: function(xhr, status, error) {
console.error('BDFG AJAX Error:', error);
},
complete: function() {
BDFG.isLoading = false;
$('.bdfg-products-wrapper').removeClass('bdfg-loading');
}
});
};

/**
* 收集过滤条件
*/
BDFG.collectFilters = function() {
var filters = {
taxonomy: {},
price: {},
rating: [],
search: '',
vendor: [],
sort: '',
page: 1
};

// 收集分类过滤
$('.bdfg-taxonomy-filter').each(function() {
var taxonomy = $(this).data('taxonomy');
filters.taxonomy[taxonomy] = [];

// 处理下拉菜单
$(this).find('select').each(function() {
var value = $(this).val();
if (value) {
filters.taxonomy[taxonomy].push(value);
}
});

// 处理复选框
$(this).find('input:checked').each(function() {
filters.taxonomy[taxonomy].push($(this).val());
});
});

// 收集价格过滤
var $minPrice = $('#bdfg-min-price'),
$maxPrice = $('#bdfg-max-price');

if ($minPrice.length && $maxPrice.length) {
filters.price = {
min: parseFloat($minPrice.val()) || '',
max: parseFloat($maxPrice.val()) || ''
};
}

// 收集评分过滤
$('.bdfg-rating-filter input:checked').each(function() {
filters.rating.push(parseInt($(this).val()));
});

// 收集搜索条件
filters.search = $('.bdfg-search-input').val();

// 收集供应商过滤
$('.bdfg-vendor-filter input:checked').each(function() {
filters.vendor.push(parseInt($(this).val()));
});

// 收集排序条件
filters.sort = $('.bdfg-sorting').val();

// 收集分页信息
filters.page = parseInt($('.bdfg-pagination .current').data('page')) || 1;

return filters;
};

/**
* 更新产品网格
*/
BDFG.updateProductsGrid = function(data) {
var $wrapper = $('.bdfg-products-wrapper');

if (data.products && data.products.length) {
var html = '<div class="bdfg-products-grid">';

data.products.forEach(function(product) {
html += BDFG.renderProductCard(product);
});

html += '</div>';

$wrapper.html(html);

// 触发自定义事件
$(document).trigger('bdfg_products_updated', [data.products]);
} else {
$wrapper.html('<p class="bdfg-no-products">' + bdfgFilters.i18n.noResults + '</p>');
}

// 滚动到产品区域
$('html, body').animate({
scrollTop: $wrapper.offset().top - 100
}, 500);
};

/**
* 渲染产品卡片
*/
BDFG.renderProductCard = function(product) {
var html = '<div class="bdfg-product-card">';
html += '<a href="' + product.permalink + '">';

if (product.thumbnail) {
html += '<img src="' + product.thumbnail + '" alt="' + product.name + '">';
}

html += '<div class="product-info">';
html += '<h4>' + product.name + '</h4>';

if (product.price_html) {
html += '<div class="bdfg-product-price">' + product.price_html + '</div>';
}

if (product.average_rating > 0) {
html += '<div class="bdfg-star-rating">';
html += BDFG.renderStarRating(product.average_rating);
html += '<span class="rating-count">(' + product.review_count + ')</span>';
html += '</div>';
}

html += '</div></a></div>';

return html;
};

/**
* 渲染星级评分
*/
BDFG.renderStarRating = function(rating) {
var html = '',
fullStars = Math.floor(rating),
halfStar = rating % 1 >= 0.5,
emptyStars = 5 - Math.ceil(rating);

// 填充星星
for (var i = 0; i < fullStars; i++) {
html += '<span class="star filled">★</span>';
}

// 半星
if (halfStar) {
html += '<span class="star half">★</span>';
}

// 空星
for (var j = 0; j < emptyStars; j++) {
html += '<span class="star">☆</span>';
}

return html;
};

/**
* 更新活动过滤器标签
*/
BDFG.updateActiveFilters = function(filters) {
var $wrapper = $('.bdfg-active-filters'),
html = '';

if (!$wrapper.length) {
return;
}

// 处理分类过滤器
Object.keys(filters.taxonomy).forEach(function(taxonomy) {
filters.taxonomy[taxonomy].forEach(function(term) {
var label = BDFG.getTermLabel(taxonomy, term);
html += BDFG.renderFilterTag('taxonomy', term, label, taxonomy);
});
});

// 处理价格过滤器
if (filters.price.min || filters.price.max) {
var priceLabel = bdfgFilters.currency_symbol;
if (filters.price.min) {
priceLabel += filters.price.min;
}
priceLabel += ' - ';
if (filters.price.max) {
priceLabel += bdfgFilters.currency_symbol + filters.price.max;
}
html += BDFG.renderFilterTag('price', 'range', priceLabel);
}

// 处理评分过滤器
filters.rating.forEach(function(rating) {
html += BDFG.renderFilterTag('rating', rating, rating + '+ ' + bdfgFilters.i18n.stars);
});

// 处理搜索条件
if (filters.search) {
html += BDFG.renderFilterTag('search', filters.search, '"' + filters.search + '"');
}

// 处理供应商过滤器
filters.vendor.forEach(function(vendorId) {
var vendorName = BDFG.getVendorName(vendorId);
html += BDFG.renderFilterTag('vendor', vendorId, vendorName);
});

if (html) {
html = '<span class="bdfg-active-filters-label">' +
bdfgFilters.i18n.activeFilters + ':</span>' + html;
html += '<a href="#" class="bdfg-clear-filters">' +
bdfgFilters.i18n.clearAll + '</a>';
}

$wrapper.html(html);
};

/**
* 渲染过滤器标签
*/
BDFG.renderFilterTag = function(type, value, label, subtype) {
var data = 'data-type="' + type + '" data-value="' + value + '"';
if (subtype) {
data += ' data-subtype="' + subtype + '"';
}

return '<span class="bdfg-filter-tag" ' + data + '>' +
label +
'<span class="remove">×</span>' +
'</span>';
};

/**
* 更新URL参数
*/
BDFG.updateUrl = function(filters) {
if (!history.pushState) {
return;
}

var params = new URLSearchParams(window.location.search);

// 清除现有的过滤参数
BDFG.filterParams.forEach(function(param) {
params.delete(param);
});

// 添加新的过滤参数
Object.keys(filters).forEach(function(key) {
var value = filters[key];
if (value && (typeof value === 'string' || value.length)) {
params.set('bdfg_' + key, JSON.stringify(value));
}
});

var newUrl = window.location.pathname;
if (params.toString()) {
newUrl += '?' + params.toString();
}

history.pushState({bdfgFilters: filters}, '', newUrl);
};

/**
* 从URL恢复过滤状态
*/
BDFG.restoreFiltersFromUrl = function() {
var params = new URLSearchParams(window.location.search);
var hasFilters = false;

BDFG.filterParams.forEach(function(param) {
if (params.has('bdfg_' + param)) {
hasFilters = true;
var value = JSON.parse(params.get('bdfg_' + param));
BDFG.setFilterValue(param, value);
}
});

if (hasFilters) {
BDFG.updateFilters();
}
};

/**
* 设置过滤器值
*/
BDFG.setFilterValue = function(type, value) {
switch (type) {
case 'taxonomy':
Object.keys(value).forEach(function(taxonomy) {
value[taxonomy].forEach(function(term) {
$('.bdfg-taxonomy-filter[data-taxonomy="' + taxonomy + '"]')
.find('input[value="' + term + '"]')
.prop('checked', true);
});
});
break;

case 'price':
if (value.min) $('#bdfg-min-price').val(value.min);
if (value.max) $('#bdfg-max-price').val(value.max);
$('#bdfg-price-slider').slider('values', [
value.min || $('#bdfg-price-slider').slider('option', 'min'),
value.max || $('#bdfg-price-slider').slider('option', 'max')
]);
break;

case 'rating':
value.forEach(function(rating) {
$('.bdfg-rating-filter input[value="' + rating + '"]')
.prop('checked', true);
});
break;

case 'search':
$('.bdfg-search-input').val(value);
break;

case 'vendor':
value.forEach(function(vendorId) {
$('.bdfg-vendor-filter input[value="' + vendorId + '"]')
.prop('checked', true);
});
break;

case 'sort':
$('.bdfg-sorting').val(value);
break;
}
};

/**
* 清除所有过滤器
*/
BDFG.clearAllFilters = function() {
// 重置所有输入
$('.bdfg-taxonomy-filter input').prop('checked', false);
$('.bdfg-taxonomy-filter select').val('');
$('.bdfg-rating-filter input').prop('checked', false);
$('.bdfg-vendor-filter input').prop('checked', false);
$('.bdfg-search-input').val('');
$('.bdfg-sorting').val(bdfgFilters.default_sort);

// 重置价格滑块
var $slider = $('#bdfg-price-slider');
if ($slider.length) {
$slider.slider('values', [
$slider.slider('option', 'min'),
$slider.slider('option', 'max')
]);
$('#bdfg-min-price').val('');
$('#bdfg-max-price').val('');
}

// 更新过滤器
BDFG.updateFilters();
};

/**
* 加载推荐
*/
BDFG.loadRecommendations = function() {
var $container = $('.bdfg-recommendations');
if (!$container.length) return;

var productId = $container.data('product-id');

$.ajax({
url: bdfgFilters.ajaxurl,
type: 'POST',
data: {
action: 'bdfg_get_recommendations',
nonce: bdfgFilters.nonce,
product_id: productId
},
success: function(response) {
if (response.success) {
BDFG.renderRecommendations(response.data);
}
}
});
};

// 定义过滤器参数
BDFG.filterParams = [
'taxonomy',
'price',
'rating',
'search',
'vendor',
'sort',
'page'
];

// 初始化
$(document).ready(function() {
BDFG.init();
});

})(jQuery);

admin/class-bdfg-settings.php

<?php
/**
* BDFG Product Filters - Admin Settings
*
* @package BDFG_Product_Filters
* @author Bei Duo Feng Ou <[email protected]>
* @version 2.5.0
*/

if (!defined('ABSPATH')) {
exit('Direct access not allowed.');
}

class BDFG_Settings {
/**
* 设置页面ID
*/
const PAGE_ID = 'bdfg-settings';

/**
* 设置选项组
*/
const OPTION_GROUP = 'bdfg_options';

/**
* 构造函数
*/
public function __construct() {
add_action('admin_menu', array($this, 'add_settings_page'));
add_action('admin_init', array($this, 'register_settings'));
add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts'));
}

/**
* 添加设置页面
*/
public function add_settings_page() {
add_submenu_page(
'woocommerce',
__('BDFG Product Filters', 'bdfg-product-filters'),
__('Product Filters', 'bdfg-product-filters'),
'manage_woocommerce',
self::PAGE_ID,
array($this, 'render_settings_page')
);
}

/**
* 注册设置
*/
public function register_settings() {
register_setting(
self::OPTION_GROUP,
'bdfg_enable_advanced_features',
array(
'type' => 'string',
'default' => 'yes'
)
);

register_setting(
self::OPTION_GROUP,
'bdfg_cache_duration',
array(
'type' => 'number',
'default' => 3600
)
);

// 添加设置区域
add_settings_section(
'bdfg_general_section',
__('General Settings', 'bdfg-product-filters'),
array($this, 'render_general_section'),
self::PAGE_ID
);

add_settings_section(
'bdfg_advanced_section',
__('Advanced Features', 'bdfg-product-filters'),
array($this, 'render_advanced_section'),
self::PAGE_ID
);

add_settings_section(
'bdfg_cache_section',
__('Cache Settings', 'bdfg-product-filters'),
array($this, 'render_cache_section'),
self::PAGE_ID
);

// 添加设置字段
add_settings_field(
'bdfg_taxonomies',
__('Product Taxonomies', 'bdfg-product-filters'),
array($this, 'render_taxonomies_field'),
self::PAGE_ID,
'bdfg_general_section'
);

add_settings_field(
'bdfg_price_filter',
__('Price Filter', 'bdfg-product-filters'),
array($this, 'render_price_filter_field'),
self::PAGE_ID,
'bdfg_general_section'
);

add_settings_field(
'bdfg_enable_advanced_features',
__('Advanced Features', 'bdfg-product-filters'),
array($this, 'render_advanced_features_field'),
self::PAGE_ID,
'bdfg_advanced_section'
);

add_settings_field(
'bdfg_cache_duration',
__('Cache Duration', 'bdfg-product-filters'),
array($this, 'render_cache_duration_field'),
self::PAGE_ID,
'bdfg_cache_section'
);
}

/**
* 渲染设置页面
*/
public function render_settings_page() {
if (!current_user_can('manage_woocommerce')) {
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>

<?php if (isset($_GET['settings-updated'])) : ?>
<div class="notice notice-success is-dismissible">
<p><?php esc_html_e('Settings saved successfully.', 'bdfg-product-filters'); ?></p>
</div>
<?php endif; ?>

<form method="post" action="options.php">
<?php
settings_fields(self::OPTION_GROUP);
do_settings_sections(self::PAGE_ID);
submit_button();
?>
</form>

<div class="bdfg-admin-footer">
<p>
<?php
printf(
__('BDFG Product Filters v%s by %s', 'bdfg-product-filters'),
BDFG_VERSION,
'<a href="https://beiduofengou.net" target="_blank">Bei Duo Feng Ou</a>'
);
?>
</p>
</div>
</div>
<?php
}

/**
* 渲染常规设置区域
*/
public function render_general_section() {
echo '<p>' . esc_html__('Configure the basic filter settings.', 'bdfg-product-filters') . '</p>';
}

/**
* 渲染高级设置区域
*/
public function render_advanced_section() {
echo '<p>' . esc_html__('Configure advanced filtering features.', 'bdfg-product-filters') . '</p>';
}

/**
* 渲染缓存设置区域
*/
public function render_cache_section() {
echo '<p>' . esc_html__('Configure caching settings to improve performance.', 'bdfg-product-filters') . '</p>';
}

/**
* 渲染分类字段
*/
public function render_taxonomies_field() {
$enabled_taxonomies = get_option('bdfg_enabled_taxonomies', array('product_cat', 'product_tag'));
$taxonomies = get_object_taxonomies('product', 'objects');

echo '<fieldset>';
foreach ($taxonomies as $taxonomy) {
printf(
'<label><input type="checkbox" name="bdfg_enabled_taxonomies[]" value="%s" %s> %s</label><br>',
esc_attr($taxonomy->name),
checked(in_array($taxonomy->name, $enabled_taxonomies), true, false),
esc_html($taxonomy->label)
);
}
echo '</fieldset>';
echo '<p class="description">' . esc_html__('Select which product taxonomies to include in filters.', 'bdfg-product-filters') . '</p>';
}

/**
* 渲染价格过滤器字段
*/
public function render_price_filter_field() {
$price_filter = get_option('bdfg_price_filter', array(
'enabled' => 'yes',
'step' => 10,
'display' => 'slider'
));

?>
<fieldset>
<label>
<input type="checkbox" name="bdfg_price_filter[enabled]"
value="yes" <?php checked($price_filter['enabled'], 'yes'); ?>>
<?php esc_html_e('Enable price filter', 'bdfg-product-filters'); ?>
</label>
<br><br>

<label>
<?php esc_html_e('Price step:', 'bdfg-product-filters'); ?>
<input type="number" name="bdfg_price_filter[step]"
value="<?php echo esc_attr($price_filter['step']); ?>"
min="1" step="1" class="small-text">
</label>
<br><br>

<label>
<?php esc_html_e('Display type:', 'bdfg-product-filters'); ?>
<select name="bdfg_price_filter[display]">
<option value="slider" <?php selected($price_filter['display'], 'slider'); ?>>
<?php esc_html_e('Slider', 'bdfg-product-filters'); ?>
</option>
<option value="input" <?php selected($price_filter['display'], 'input'); ?>>
<?php esc_html_e('Input fields', 'bdfg-product-filters'); ?>
</option>
<option value="both" <?php selected($price_filter['display'], 'both'); ?>>
<?php esc_html_e('Both', 'bdfg-product-filters'); ?>
</option>
</select>
</label>
</fieldset>
<?php
}

/**
* 渲染高级功能字段
*/
public function render_advanced_features_field() {
$advanced_features = get_option('bdfg_enable_advanced_features', 'yes');

?>
<fieldset>
<label>
<input type="checkbox" name="bdfg_enable_advanced_features"
value="yes" <?php checked($advanced_features, 'yes'); ?>>
<?php esc_html_e('Enable advanced filtering features', 'bdfg-product-filters'); ?>
</label>
<p class="description">
<?php esc_html_e('Includes vendor filtering, smart recommendations, and more.', 'bdfg-product-filters'); ?>
</p>
</fieldset>
<?php
}

/**
* 渲染缓存时间字段
*/
public function render_cache_duration_field() {
$cache_duration = get_option('bdfg_cache_duration', 3600);

?>
<fieldset>
<input type="number" name="bdfg_cache_duration"
value="<?php echo esc_attr($cache_duration); ?>"
min="0" step="60" class="medium-text">
<p class="description">
<?php esc_html_e('Cache duration in seconds. Set to 0 to disable caching.', 'bdfg-product-filters'); ?>
</p>
</fieldset>
<?php
}

/**
* 加载管理页面脚本和样式
*/
public function enqueue_admin_scripts($hook) {
if ($hook !== 'woocommerce_page_' . self::PAGE_ID) {
return;
}

wp_enqueue_style(
'bdfg-admin-style',
BDFG_ASSETS_URL . 'css/admin.css',
array(),
BDFG_VERSION
);

wp_enqueue_script(
'bdfg-admin-script',
BDFG_ASSETS_URL . 'js/admin.js',
array('jquery'),
BDFG_VERSION,
true
);

wp_localize_script('bdfg-admin-script', 'bdfgAdmin', array(
'ajaxurl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('bdfg-admin'),
'i18n' => array(
'confirm_reset' => __('Are you sure you want to reset all settings to default?', 'bdfg-product-filters'),
'success' => __('Settings saved successfully.', 'bdfg-product-filters'),
'error' => __('An error occurred while saving settings.', 'bdfg-product-filters')
)
));
}
}

// 初始化设置
new BDFG_Settings();

assets/css/admin.css

相关文章: WordPress优惠券提醒插件系统

/**
* BDFG Product Filters - Admin Styles
*
* @package BDFG_Product_Filters
* @author Bei Duo Feng Ou <[email protected]>
* @version 2.5.0
*/

/* 设置页面样式 */
.bdfg-settings-wrap {
max-width: 1200px;
margin: 20px 0;
}

.bdfg-settings-header {
background: #fff;
padding: 20px;
margin-bottom: 20px;
border: 1px solid #ddd;
border-radius: 4px;
}

.bdfg-settings-header h1 {
margin: 0;
color: #23282d;
font-size: 24px;
font-weight: 400;
}

/* 表单样式 */
.bdfg-form-table {
background: #fff;
border: 1px solid #ddd;
border-radius: 4px;
margin-top: 20px;
}

.bdfg-form-table th {
padding: 20px;
font-weight: 600;
}

.bdfg-form-table td {
padding: 15px 20px;
}

/* 字段样式 */
.bdfg-field-wrap {
margin-bottom: 15px;
}

.bdfg-field-label {
display: block;
margin-bottom: 5px;
font-weight: 600;
}

.bdfg-field-description {
color: #666;
font-style: italic;
margin-top: 5px;
}

/* 复选框组样式 */
.bdfg-checkbox-group {
max-height: 200px;
overflow-y: auto;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
}

.bdfg-checkbox-item {
margin-bottom: 8px;
}

.bdfg-checkbox-item:last-child {
margin-bottom: 0;
}

/* 标签样式 */
.bdfg-tag {
display: inline-block;
padding: 4px 8px;
margin: 0 5px 5px 0;
background: #f0f0f0;
border-radius: 3px;
font-size: 12px;
}

.bdfg-tag .remove {
margin-left: 5px;
color: #999;
cursor: pointer;
}

/* 按钮样式 */
.bdfg-button {
display: inline-block;
padding: 8px 15px;
background: #2271b1;
color: #fff;
border: none;
border-radius: 3px;
cursor: pointer;
text-decoration: none;
font-size: 13px;
line-height: 1.5;
}

.bdfg-button:hover {
background: #135e96;
color: #fff;
}

.bdfg-button.reset {
background: #dc3232;
}

.bdfg-button.reset:hover {
background: #ba281e;
}

/* 通知样式 */
.bdfg-notice {
padding: 12px;
margin: 15px 0;
background: #fff;
border-left: 4px solid #00a32a;
box-shadow: 0 1px 1px rgba(0,0,0,.04);
}

.bdfg-notice.error {
border-left-color: #dc3232;
}

/* 页脚样式 */
.bdfg-admin-footer {
margin-top: 30px;
padding-top: 20px;
border-top: 1px solid #ddd;
color: #666;
font-style: italic;
}

/* 响应式调整 */
@media screen and (max-width: 782px) {
.bdfg-form-table th {
padding: 15px;
}

.bdfg-form-table td {
padding: 10px 15px;
}

.bdfg-checkbox-group {
max-height: 150px;
}
}

assets/js/admin.js

/**
* BDFG Product Filters - Admin JavaScript
*
* @package BDFG_Product_Filters
* @author Bei Duo Feng Ou <[email protected]>
* @version 2.5.0
*/

(function($) {
'use strict';

var BDFG_Admin = {
init: function() {
this.bindEvents();
this.initTooltips();
this.initSortable();
},

bindEvents: function() {
// 重置设置
$('.bdfg-reset-settings').on('click', this.resetSettings);

// 测试缓存
$('.bdfg-test-cache').on('click', this.testCache);

// 清除缓存
$('.bdfg-clear-cache').on('click', this.clearCache);

// 导入/导出设置
$('#bdfg-import-settings').on('change', this.importSettings);
$('.bdfg-export-settings').on('click', this.exportSettings);

// 动态添加/删除字段
$('.bdfg-add-field').on('click', this.addField);
$(document).on('click', '.bdfg-remove-field', this.removeField);

// 表单验证
$('#bdfg-settings-form').on('submit', this.validateForm);
},

initTooltips: function() {
$('.bdfg-tooltip').tooltipster({
theme: 'tooltipster-light',
maxWidth: 300,
animation: 'fade',
delay: 200
});
},

initSortable: function() {
$('.bdfg-sortable').sortable({
handle: '.bdfg-sort-handle',
axis: 'y',
update: function() {
BDFG_Admin.saveOrder();
}
});
},

resetSettings: function(e) {
e.preventDefault();

if (!confirm(bdfgAdmin.i18n.confirm_reset)) {
return;
}

$.ajax({
url: bdfgAdmin.ajaxurl,
type: 'POST',
data: {
action: 'bdfg_reset_settings',
nonce: bdfgAdmin.nonce
},
beforeSend: function() {
BDFG_Admin.showLoader();
},
success: function(response) {
if (response.success) {
window.location.reload();
} else {
BDFG_Admin.showError(response.data.message);
}
},
error: function() {
BDFG_Admin.showError(bdfgAdmin.i18n.error);
},
complete: function() {
BDFG_Admin.hideLoader();
}
});
},

testCache: function(e) {
e.preventDefault();

$.ajax({
url: bdfgAdmin.ajaxurl,
type: 'POST',
data: {
action: 'bdfg_test_cache',
nonce: bdfgAdmin.nonce
},
beforeSend: function() {
BDFG_Admin.showLoader();
},
success: function(response) {
if (response.success) {
BDFG_Admin.showSuccess(response.data.message);
} else {
BDFG_Admin.showError(response.data.message);
}
},
error: function() {
BDFG_Admin.showError(bdfgAdmin.i18n.error);
},
complete: function() {
BDFG_Admin.hideLoader();
}
});
},

clearCache: function(e) {
e.preventDefault();

$.ajax({
url: bdfgAdmin.ajaxurl,
type: 'POST',
data: {
action: 'bdfg_clear_cache',
nonce: bdfgAdmin.nonce
},
beforeSend: function() {
BDFG_Admin.showLoader();
},
success: function(response) {
if (response.success) {
BDFG_Admin.showSuccess(response.data.message);
} else {
BDFG_Admin.showError(response.data.message);
}
},
error: function() {
BDFG_Admin.showError(bdfgAdmin.i18n.error);
},
complete: function() {
BDFG_Admin.hideLoader();
}
});
},

importSettings: function(e) {
var file = e.target.files[0];
if (!file) return;

var reader = new FileReader();
reader.onload = function(e) {
try {
var settings = JSON.parse(e.target.result);

$.ajax({
url: bdfgAdmin.ajaxurl,
type: 'POST',
data: {
action: 'bdfg_import_settings',
nonce: bdfgAdmin.nonce,
settings: settings
},
beforeSend: function() {
BDFG_Admin.showLoader();
},
success: function(response) {
if (response.success) {
window.location.reload();
} else {
BDFG_Admin.showError(response.data.message);
}
},
error: function() {
BDFG_Admin.showError(bdfgAdmin.i18n.error);
},
complete: function() {
BDFG_Admin.hideLoader();
}
});
} catch (error) {
BDFG_Admin.showError(bdfgAdmin.i18n.invalid_file);
}
};
reader.readAsText(file);
},

exportSettings: function(e) {
e.preventDefault();

$.ajax({
url: bdfgAdmin.ajaxurl,
type: 'POST',
data: {
action: 'bdfg_export_settings',
nonce: bdfgAdmin.nonce
},
beforeSend: function() {
BDFG_Admin.showLoader();
},
success: function(response) {
if (response.success) {
var dataStr = "data:text/json;charset=utf-8," +
encodeURIComponent(JSON.stringify(response.data));
var downloadLink = document.createElement('a');
downloadLink.setAttribute("href", dataStr);
downloadLink.setAttribute("download", "bdfg-settings.json");
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
} else {
BDFG_Admin.showError(response.data.message);
}
},
error: function() {
BDFG_Admin.showError(bdfgAdmin.i18n.error);
},
complete: function() {
BDFG_Admin.hideLoader();
}
});
},

addField: function(e) {
e.preventDefault();

var template = wp.template('bdfg-field');
var $container = $(this).closest('.bdfg-fields-container');
var count = $container.find('.bdfg-field').length;

$container.append(template({
index: count,
timestamp: new Date().getTime()
}));
},

removeField: function(e) {
e.preventDefault();

$(this).closest('.bdfg-field').fadeOut(300, function() {
$(this).remove();
BDFG_Admin.reindexFields();
});
},

reindexFields: function() {
$('.bdfg-fields-container').each(function() {
$(this).find('.bdfg-field').each(function(index) {
$(this).find('[name]').each(function() {
var name = $(this).attr('name');
$(this).attr('name', name.replace(/\[\d+\]/, '[' + index + ']'));
});
});
});
},

saveOrder: function() {
var order = [];
$('.bdfg-sortable .bdfg-field').each(function() {
order.push($(this).data('id'));
});

$.ajax({
url: bdfgAdmin.ajaxurl,
type: 'POST',
data: {
action: 'bdfg_save_order',
nonce: bdfgAdmin.nonce,
order: order
},
success: function(response) {
if (response.success) {
BDFG_Admin.showSuccess(response.data.message);
} else {
BDFG_Admin.showError(response.data.message);
}
}
});
},

validateForm: function(e) {
var isValid = true;

// 清除之前的错误
$('.bdfg-error').remove();

// 验证必填字段
$(this).find('[required]').each(function() {
if (!$(this).val()) {
isValid = false;
$(this).after('<span class="bdfg-error">' +
bdfgAdmin.i18n.required_field + '</span>');
}
});

// 验证数字字段
$(this).find('[type="number"]').each(function() {
var val = $(this).val();
var min = $(this).attr('min');
var max = $(this).attr('max');

if (val && (
(min && parseFloat(val) < parseFloat(min)) ||
(max && parseFloat(val) > parseFloat(max))
)) {
isValid = false;
$(this).after('<span class="bdfg-error">' +
bdfgAdmin.i18n.invalid_number + '</span>');
}
});

if (!isValid) {
e.preventDefault();
$('html, body').animate({
scrollTop: $('.bdfg-error').first().offset().top - 100
}, 500);
}
},

showLoader: function() {
$('.bdfg-loader').fadeIn(200);
},

hideLoader: function() {
$('.bdfg-loader').fadeOut(200);
},

showSuccess: function(message) {
var $notice = $('<div class="bdfg-notice success">' + message + '</div>')
.hide()
.insertBefore('#bdfg-settings-form')
.fadeIn(300);

setTimeout(function() {
$notice.fadeOut(300, function() {
$(this).remove();
});
}, 3000);
},

showError: function(message) {
var $notice = $('<div class="bdfg-notice error">' + message + '</div>')
.hide()
.insertBefore('#bdfg-settings-form')
.fadeIn(300);

setTimeout(function() {
$notice.fadeOut(300, function() {
$(this).remove();
});
}, 5000);
}
};

// 文档加载完成后初始化
$(document).ready(function() {
BDFG_Admin.init();
});

})(jQuery);

 

相关文章: WooCommerce 高级产品定价和折扣管理

Leave a Comment