我开发了自定义插件,为我的网站用户提供对产品投赞成票/反对票的功能,并且根据投票权重,该插件会将较高排名产品的价格提高到 10000,将最低排名提高到 1 以及介于两者之间的所有其他产品。此功能按预期工作,但有一个小问题,在更新每个价格后,在商店和仪表板/产品部分中,产品排序顺序行为不正确。假设 1 美元的产品更改为 10000,那么它现在应该转到升序的最后一个位置,但它仍然在同一位置。我做了很多调试,发现了这两条线索 1.手动更新价格排序反映正确且立即。 2. 在插件更新的产品上,当我手动重新生成查找表但我想自动化该过程时,排序顺序会更正。
我已添加这样做,但网站的行为陈旧且相同,需要帮助来解决此问题。这是主要代码
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
class WC_Vote_Price {
private static $instance = null;
public static function instance() {
if ( is_null( self::$instance ) ) {
self::$instance = new self();
}
return self::$instance;
}
public function __construct() {
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
add_action( 'woocommerce_single_product_summary', array( $this, 'display_vote_buttons' ), 25 );
add_action( 'wp_ajax_wc_vote_price', array( $this, 'handle_vote' ) );
add_action( 'wp_ajax_nopriv_wc_vote_price', array( $this, 'handle_vote' ) );
add_action( 'admin_menu', array( $this, 'add_settings_page' ) );
add_action( 'admin_post_wc_vote_price_reset', array( $this, 'handle_reset' ) );
add_filter( 'woocommerce_get_price_html', array( $this, 'custom_woocommerce_get_price_html'), 10, 2 );
add_action('save_post_product', array($this, 'handle_product_price_update'), 10, 3);
}
public function handle_product_price_update($post_id, $post, $update) {
if ($post->post_type !== 'product') {
return;
}
// Ensure we only run on price updates
if (isset($_POST['meta_input'])) {
$meta_input = $_POST['meta_input'];
if (isset($meta_input['_price'])) {
try {
wc_update_product_lookup_tables();
wc_delete_product_transients(); // Clear WooCommerce product cache
// Forcefully regenerate lookup tables
wc_get_container()->get( \Automattic\WooCommerce\Internal\Product\Lookup\LookupDataStore::class )->regenerate();
} catch (Exception $e) {
error_log('Error updating lookup tables: ' . $e->getMessage());
}
}
}
}
public function enqueue_scripts() {
wp_enqueue_style( 'wc-vote-price-style', plugins_url( '../assets/css/style.css', __FILE__ ) );
wp_enqueue_script( 'wc-vote-price-script', plugins_url( '../assets/js/script.js', __FILE__ ), array( 'jquery' ), null, true );
wp_localize_script( 'wc-vote-price-script', 'wc_vote_price_params', array(
'ajax_url' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'wc-vote-price-nonce' )
) );
}
public function display_vote_buttons() {
global $product;
$votes = get_post_meta( $product->get_id(), '_wc_vote_price_votes', true ) ?: 0;
$downvotes = get_post_meta( $product->get_id(), '_wc_vote_price_downvotes', true ) ?: 0;
$user_ip = $_SERVER['REMOTE_ADDR'];
$voted_up = get_post_meta( $product->get_id(), '_wc_vote_price_voted_up_' . $user_ip, true );
$voted_down = get_post_meta( $product->get_id(), '_wc_vote_price_voted_down_' . $user_ip, true );
$upvote_class = $voted_up ? 'voted' : '';
$downvote_class = $voted_down ? 'voted' : '';
echo '<div class="wc-vote-price">';
echo '<button class="vote-button ' . esc_attr( $upvote_class ) . '" data-vote="up" data-product="' . esc_attr( $product->get_id() ) . '">Vote Up (' . esc_html( $votes ) . ')</button>';
echo '<button class="vote-button ' . esc_attr( $downvote_class ) . '" data-vote="down" data-product="' . esc_attr( $product->get_id() ) . '">Vote Down (' . esc_html( $downvotes ) . ')</button>';
echo '</div>';
echo '<div id="loading-overlay"><div class="loading-message">Your vote is being processed<span class="dots"><span></span><span></span><span></span></span></div></div>';
}
public function handle_vote() {
check_ajax_referer( 'wc-vote-price-nonce', 'nonce' );
if ( ! isset( $_POST['product_id'], $_POST['vote_type'] ) ) {
wp_send_json_error( 'Invalid data' );
}
$product_id = absint( $_POST['product_id'] );
$vote_type = sanitize_text_field( $_POST['vote_type'] );
$user_ip = $_SERVER['REMOTE_ADDR'];
$voted_up = get_post_meta( $product_id, '_wc_vote_price_voted_up_' . $user_ip, true );
$voted_down = get_post_meta( $product_id, '_wc_vote_price_voted_down_' . $user_ip, true );
$votes = get_post_meta( $product_id, '_wc_vote_price_votes', true ) ?: 0;
$downvotes = get_post_meta( $product_id, '_wc_vote_price_downvotes', true ) ?: 0;
if ( $vote_type === 'up' ) {
if ( $voted_up ) {
$votes--;
delete_post_meta( $product_id, '_wc_vote_price_voted_up_' . $user_ip );
} else {
if ( $voted_down ) {
$downvotes--;
delete_post_meta( $product_id, '_wc_vote_price_voted_down_' . $user_ip );
}
$votes++;
update_post_meta( $product_id, '_wc_vote_price_voted_up_' . $user_ip, true );
}
} elseif ( $vote_type === 'down' ) {
if ( $voted_down ) {
$downvotes--;
delete_post_meta( $product_id, '_wc_vote_price_voted_down_' . $user_ip );
} else {
if ( $voted_up ) {
$votes--;
delete_post_meta( $product_id, '_wc_vote_price_voted_up_' . $user_ip );
}
$downvotes++;
update_post_meta( $product_id, '_wc_vote_price_voted_down_' . $user_ip, true );
}
}
update_post_meta( $product_id, '_wc_vote_price_votes', $votes );
update_post_meta( $product_id, '_wc_vote_price_downvotes', $downvotes );
// Trigger price adjustment
$this->adjust_product_prices();
wc_delete_product_transients();
wp_send_json_success( array( 'votes' => $votes, 'downvotes' => $downvotes ) );
}
private function adjust_product_prices() {
global $wpdb;
// Fetch all products and their votes
$products = $wpdb->get_results("
SELECT p.ID, COALESCE(v.meta_value, 0) AS votes
FROM {$wpdb->posts} p
LEFT JOIN {$wpdb->postmeta} v ON p.ID = v.post_id AND v.meta_key = '_wc_vote_price_votes'
WHERE p.post_type = 'product' AND p.post_status = 'publish'
ORDER BY votes DESC, p.ID ASC
");
$max_price = 10000;
$min_price = 1;
$price_range = $max_price - $min_price;
$num_products = count($products);
if ($num_products == 0) {
return; // No products to adjust
}
$rank = 0;
foreach ($products as $product) {
$sale_price = $max_price - ($rank * $price_range / ($num_products - 1));
$sale_price = round($sale_price);
$regular_price = $sale_price + 1;
// Update sale price and regular price
$wpdb->update(
$wpdb->postmeta,
array( 'meta_value' => $sale_price ),
array( 'post_id' => $product->ID, 'meta_key' => '_sale_price' ),
array( '%d' ),
array( '%d', '%s' )
);
$wpdb->update(
$wpdb->postmeta,
array( 'meta_value' => $regular_price ),
array( 'post_id' => $product->ID, 'meta_key' => '_regular_price' ),
array( '%d' ),
array( '%d', '%s' )
);
update_post_meta($product->ID, '_price', $sale_price);
update_post_meta($product->ID, '_wc_vote_price_adjusted_price', $sale_price);
$rank++;
// Logging the price update
error_log("Updating product ID {$product->ID} to sale price {$sale_price} and regular price {$regular_price}");
}
wc_delete_product_transients();
// Clear sorting transients
$transients = array(
'wc_products_onsale',
'wc_layered_nav_counts',
'wc_products_will_display',
'wc_hidden_term_ids'
);
foreach ( $transients as $transient ) {
delete_transient( $transient );
}
// Ensure the class exists before regenerating lookup tables
if (class_exists('Automattic\WooCommerce\Internal\Product\Lookup\LookupDataStore')) {
try {
wc_get_container()->get( \Automattic\WooCommerce\Internal\Product\Lookup\LookupDataStore::class )->regenerate();
} catch (Exception $e) {
error_log('Error regenerating lookup tables: ' . $e->getMessage());
}
} else {
error_log('LookupDataStore class does not exist.');
}
}
public function custom_woocommerce_get_price_html($price, $product) {
$adjusted_price = get_post_meta($product->get_id(), '_wc_vote_price_adjusted_price', true);
if ($adjusted_price) {
$price = wc_price($adjusted_price);
}
return $price;
}
public function add_settings_page() {
add_menu_page(
'WC Vote Price Settings',
'Vote Price',
'manage_options',
'wc-vote-price',
array( $this, 'settings_page_content' ),
'dashicons-thumbs-up',
56
);
}
public function settings_page_content() {
?>
<div class="wrap">
<h1>Vote Price Settings</h1>
<form action="admin-post.php" method="post">
<input type="hidden" name="action" value="wc_vote_price_reset">
<button type="submit" class="button button-primary">Reset All Votes</button>
</form>
</div>
<?php
}
public function handle_reset() {
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
global $wpdb;
$wpdb->query("
DELETE FROM {$wpdb->postmeta}
WHERE meta_key IN ('_wc_vote_price_votes', '_wc_vote_price_downvotes')
");
$wpdb->query("
DELETE FROM {$wpdb->postmeta}
WHERE meta_key LIKE '_wc_vote_price_voted_%'
");
// Reset prices
$this->adjust_product_prices();
// Redirect back to the settings page
wp_redirect( admin_url( 'admin.php?page=wc-vote-price&reset=true' ) );
exit;
}
}
WC_Vote_Price::instance();
?>
我已停用所有插件,使用主题二十二十四。
在尝试重新生成查找表和清除瞬态时,您似乎走在正确的轨道上,但可能有一些地方可能需要调整或更改,以确保产品排序正确更新。
您可以使用
woocommerce_update_product
操作而不是 save_post_product
。此操作专门用于管理产品数据的更改。
add_action('woocommerce_update_product', array($this,'handle_product_price_update'), 10, 1);
这可确保您的代码在产品数据更新(包括价格变化)时运行。
既然您注意到手动更新可以正常工作,请确保您的
adjust_product_prices
方法可以正确处理数据库更新。如果操作不正确,立即更新数据库可能会导致差异。确保所有相关元键 (_price, _sale_price, and _regular_price)
均已正确更新。
由于您已禁用其他插件并使用默认主题,请确保不存在可能影响排序的剩余自定义或覆盖。自定义主题或代码可能会影响 WooCommerce 的排序方式。