| @@ -33,9 +33,11 @@ | ||
| 33 | 33 | public function __construct(?int $user_id = null, ?string $session_key = null) |
| 34 | 34 | { |
| 35 | 35 | $this->session = new Wishlist_Session(); |
| 36 | 36 | $this->user_id = $user_id ?? get_current_user_id(); |
| 37 | - $this->session_key = $session_key ?: $this->session->get_session_key(); | |
| 37 | + $this->session_key = ($session_key !== null && $session_key !== '') | |
| 38 | + ? $session_key | |
| 39 | + : $this->session->get_session_key(false); | |
| 38 | 40 | $this->active_wishlist_id = $this->resolve_active_wishlist_id(); |
| 39 | 41 | } |
| 40 | 42 | |
| 41 | 43 | /** |
| @@ -72,8 +74,10 @@ | ||
| 72 | 74 | * @return array|WP_Error Operation result. |
| 73 | 75 | */ |
| 74 | 76 | public function add_item(int $product_id, int $variation_id = 0, int $qty = 1, ?string $wishlist_id = null) |
| 75 | 77 | { |
| 78 | + $this->ensure_guest_session(); | |
| 79 | + | |
| 76 | 80 | $validation = $this->validate_product($product_id, $variation_id); |
| 77 | 81 | if (is_wp_error($validation)) { |
| 78 | 82 | return $validation; |
| 79 | 83 | } |
| @@ -193,8 +197,12 @@ | ||
| 193 | 197 | * @return array<int, object> List of wishlist rows. |
| 194 | 198 | */ |
| 195 | 199 | public function get_items(?string $wishlist_id = null): array |
| 196 | 200 | { |
| 201 | + if ($this->is_anonymous_guest()) { | |
| 202 | + return []; | |
| 203 | + } | |
| 204 | + | |
| 197 | 205 | $wishlist_id = $this->normalize_wishlist_id($wishlist_id); |
| 198 | 206 | global $wpdb; |
| 199 | 207 | |
| 200 | 208 | $where = $this->get_scope_where($wishlist_id); |
| @@ -227,8 +235,12 @@ | ||
| 227 | 235 | * @return array<int, object> Lists rows. |
| 228 | 236 | */ |
| 229 | 237 | public function get_lists(): array |
| 230 | 238 | { |
| 239 | + if ($this->is_anonymous_guest()) { | |
| 240 | + return []; | |
| 241 | + } | |
| 242 | + | |
| 231 | 243 | global $wpdb; |
| 232 | 244 | |
| 233 | 245 | $lists_table = Wishlist_DB::get_lists_table(); |
| 234 | 246 | $where = $this->user_id > 0 |
| @@ -241,9 +253,17 @@ | ||
| 241 | 253 | $this->ensure_default_list(self::DEFAULT_WISHLIST_ID); |
| 242 | 254 | $lists = $wpdb->get_results("SELECT * FROM {$lists_table} WHERE {$where} ORDER BY created_at DESC"); |
| 243 | 255 | } |
| 244 | 256 | |
| 245 | - return $lists ?: []; | |
| 257 | + $unique = []; | |
| 258 | + foreach ($lists ?: [] as $list) { | |
| 259 | + $key = (string) ($list->slug ?: $list->id); | |
| 260 | + if (!isset($unique[$key])) { | |
| 261 | + $unique[$key] = $list; | |
| 262 | + } | |
| 263 | + } | |
| 264 | + | |
| 265 | + return array_values($unique); | |
| 246 | 266 | } |
| 247 | 267 | |
| 248 | 268 | /** |
| 249 | 269 | * Create a new wishlist record. |
| @@ -253,8 +273,10 @@ | ||
| 253 | 273 | * @return array<string, mixed>|WP_Error Created list data or error. |
| 254 | 274 | */ |
| 255 | 275 | public function create_list(string $title, string $visibility = 'private') |
| 256 | 276 | { |
| 277 | + $this->ensure_guest_session(); | |
| 278 | + | |
| 257 | 279 | global $wpdb; |
| 258 | 280 | |
| 259 | 281 | if (empty($title)) { |
| 260 | 282 | return new WP_Error('wishlist_title_missing', esc_html__('List title is required.', 'king-addons')); |
| @@ -469,8 +491,12 @@ | ||
| 469 | 491 | * @return int Count of items. |
| 470 | 492 | */ |
| 471 | 493 | public function get_count(?string $wishlist_id = null, bool $force_refresh = false): int |
| 472 | 494 | { |
| 495 | + if ($this->is_anonymous_guest()) { | |
| 496 | + return 0; | |
| 497 | + } | |
| 498 | + | |
| 473 | 499 | $wishlist_id = $this->normalize_wishlist_id($wishlist_id); |
| 474 | 500 | $cache_key = $this->get_cache_key($wishlist_id); |
| 475 | 501 | $cache_enabled = Wishlist_Settings::get('cache_enabled', false); |
| 476 | 502 | $cache_ttl = max(0, intval(Wishlist_Settings::get('cache_ttl', self::CACHE_TTL))); |
| @@ -605,20 +631,36 @@ | ||
| 605 | 631 | * @return void |
| 606 | 632 | */ |
| 607 | 633 | public function ensure_default_list(string $wishlist_id): void |
| 608 | 634 | { |
| 635 | + if ($this->user_id <= 0) { | |
| 636 | + $this->ensure_guest_session(); | |
| 637 | + if ($this->session_key === '') { | |
| 638 | + return; | |
| 639 | + } | |
| 640 | + } | |
| 641 | + | |
| 609 | 642 | global $wpdb; |
| 610 | 643 | |
| 611 | 644 | $lists_table = Wishlist_DB::get_lists_table(); |
| 612 | 645 | |
| 613 | - $existing_row = $wpdb->get_var( | |
| 614 | - $wpdb->prepare( | |
| 615 | - "SELECT id FROM {$lists_table} WHERE (user_id = %d OR session_key = %s) AND slug = %s LIMIT 1", | |
| 616 | - $this->user_id, | |
| 617 | - $this->session_key, | |
| 618 | - $wishlist_id | |
| 619 | - ) | |
| 620 | - ); | |
| 646 | + if ($this->user_id > 0) { | |
| 647 | + $existing_row = $wpdb->get_var( | |
| 648 | + $wpdb->prepare( | |
| 649 | + "SELECT id FROM {$lists_table} WHERE user_id = %d AND slug = %s LIMIT 1", | |
| 650 | + $this->user_id, | |
| 651 | + $wishlist_id | |
| 652 | + ) | |
| 653 | + ); | |
| 654 | + } else { | |
| 655 | + $existing_row = $wpdb->get_var( | |
| 656 | + $wpdb->prepare( | |
| 657 | + "SELECT id FROM {$lists_table} WHERE user_id = 0 AND session_key = %s AND slug = %s LIMIT 1", | |
| 658 | + $this->session_key, | |
| 659 | + $wishlist_id | |
| 660 | + ) | |
| 661 | + ); | |
| 662 | + } | |
| 621 | 663 | |
| 622 | 664 | if ($existing_row) { |
| 623 | 665 | return; |
| 624 | 666 | } |
| @@ -647,8 +689,12 @@ | ||
| 647 | 689 | * @return object|null Wishlist row. |
| 648 | 690 | */ |
| 649 | 691 | private function get_item_row(string $wishlist_id, int $product_id, int $variation_id): ?object |
| 650 | 692 | { |
| 693 | + if ($this->is_anonymous_guest()) { | |
| 694 | + return null; | |
| 695 | + } | |
| 696 | + | |
| 651 | 697 | global $wpdb; |
| 652 | 698 | $table = Wishlist_DB::get_items_table(); |
| 653 | 699 | $where = $this->get_scope_where($wishlist_id); |
| 654 | 700 | $where['sql'] .= ' AND product_id = %d AND variation_id = %d'; |
| @@ -678,8 +724,32 @@ | ||
| 678 | 724 | return new WP_Error('wishlist_invalid_product', esc_html__('Product not found.', 'king-addons')); |
| 679 | 725 | } |
| 680 | 726 | |
| 681 | 727 | return true; |
| 728 | + } | |
| 729 | + | |
| 730 | + /** | |
| 731 | + * Whether this request is a guest with no session cookie yet. | |
| 732 | + * | |
| 733 | + * @return bool True when reads should stay empty and skip Set-Cookie. | |
| 734 | + */ | |
| 735 | + private function is_anonymous_guest(): bool | |
| 736 | + { | |
| 737 | + return $this->user_id <= 0 && $this->session_key === ''; | |
| 738 | + } | |
| 739 | + | |
| 740 | + /** | |
| 741 | + * Mint a guest cookie only when a write actually needs identity. | |
| 742 | + * | |
| 743 | + * @return void | |
| 744 | + */ | |
| 745 | + private function ensure_guest_session(): void | |
| 746 | + { | |
| 747 | + if ($this->user_id > 0 || $this->session_key !== '') { | |
| 748 | + return; | |
| 749 | + } | |
| 750 | + | |
| 751 | + $this->session_key = $this->session->get_session_key(true); | |
| 682 | 752 | } |
| 683 | 753 | |
| 684 | 754 | /** |
| 685 | 755 | * Build scope-aware WHERE clause for queries. |