Selaa lähdekoodia

Fix UPVP attribute discovery for product-level "Used for variations" attributes

Variant attribute combobox was empty when the only variation-eligible
attribute was a custom (non-global) one defined on the product's
Attributes tab — Source 1 (global taxonomies) and Source 2 (existing
variation postmeta) couldn't see it.

- get_variation_attribute_heads(): added Source 3 that scans every
  variable parent's _product_attributes and includes any entry with
  is_variation=1 (taxonomy or custom). Also corrects Source 2's
  is_taxonomy flag.
- get_variation_attribute_values(): falls back to parsing pipe-
  separated values from _product_attributes['<key>']['value'] (or
  resolving term IDs for taxonomy attributes) when no variation
  postmeta exists yet.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Dalibor Votruba 2 kuukautta sitten
vanhempi
commit
8cb3e316ca

+ 108 - 10
studiou-wc-product-cat-manage/includes/class-studiou-wc-product-cat-manage-manipulator.php

@@ -1526,7 +1526,7 @@ class Studiou_WC_Product_Cat_Manage_Manipulator {
             }
         }
 
-        // Source 2: custom (non-taxonomy) variation attributes.
+        // Source 2: existing variation postmeta (attribute_*).
         $sql = "
             SELECT DISTINCT pm.meta_key
               FROM {$wpdb->postmeta} pm
@@ -1542,21 +1542,54 @@ class Studiou_WC_Product_Cat_Manage_Manipulator {
                 if ($slug === '' || isset($seen[$slug])) {
                     continue;
                 }
-                // Already included as taxonomy with same slug? — prefer the
-                // taxonomy entry (already set under 'pa_<slug>'), so skip if
-                // this slug starts with 'pa_'.
-                if (strpos($slug, 'pa_') === 0 && isset($seen[$slug])) {
-                    continue;
-                }
                 $seen[$slug] = true;
                 $heads[] = array(
                     'key'         => $slug,
                     'label'       => wc_attribute_label($slug),
-                    'is_taxonomy' => false,
+                    'is_taxonomy' => (strpos($slug, 'pa_') === 0) || taxonomy_exists($slug),
                 );
             }
         }
 
+        // Source 3: variable parent products' _product_attributes postmeta.
+        // Catches attributes flagged "Used for variations" on the product's
+        // Attributes tab, even when (a) the attribute is a custom non-global
+        // entry, or (b) no variations have been generated yet.
+        $sql_parents = "
+            SELECT pm.meta_value
+              FROM {$wpdb->postmeta} pm
+              INNER JOIN {$wpdb->posts} p ON p.ID = pm.post_id
+             WHERE p.post_type = 'product'
+               AND p.post_status IN ('publish','private','draft')
+               AND pm.meta_key = '_product_attributes'
+        ";
+        $serialized_rows = $wpdb->get_col($sql_parents);
+        if (!empty($serialized_rows)) {
+            foreach ($serialized_rows as $serialized) {
+                $attrs = maybe_unserialize($serialized);
+                if (!is_array($attrs)) {
+                    continue;
+                }
+                foreach ($attrs as $attr_key => $attr_data) {
+                    if (!is_array($attr_data) || empty($attr_data['is_variation'])) {
+                        continue;
+                    }
+                    $is_taxonomy = !empty($attr_data['is_taxonomy']);
+                    $name = isset($attr_data['name']) ? $attr_data['name'] : $attr_key;
+                    $key  = $is_taxonomy ? $name : sanitize_title($name);
+                    if ($key === '' || isset($seen[$key])) {
+                        continue;
+                    }
+                    $seen[$key] = true;
+                    $heads[] = array(
+                        'key'         => $key,
+                        'label'       => $is_taxonomy ? wc_attribute_label($key) : $name,
+                        'is_taxonomy' => $is_taxonomy,
+                    );
+                }
+            }
+        }
+
         // Sort alphabetically by label.
         usort($heads, function($a, $b) {
             return strcasecmp($a['label'], $b['label']);
@@ -1622,11 +1655,76 @@ class Studiou_WC_Product_Cat_Manage_Manipulator {
         ";
 
         $values = $wpdb->get_col($wpdb->prepare($sql, ...$params));
+
+        $is_taxonomy = (strpos($attribute_key, 'pa_') === 0) || taxonomy_exists($attribute_key);
+
+        // Fallback: if SQL returned no values but the attribute exists on a
+        // variable parent's _product_attributes (e.g. variations not generated
+        // yet, or custom attribute), parse pipe-separated values from there.
         if (empty($values)) {
-            return array();
+            $sanitized_key = $is_taxonomy
+                ? (strpos($attribute_key, 'pa_') === 0 ? $attribute_key : 'pa_' . $attribute_key)
+                : sanitize_title($attribute_key);
+
+            $sql_parents = "
+                SELECT pm.meta_value
+                  FROM {$wpdb->postmeta} pm
+                  INNER JOIN {$wpdb->posts} p ON p.ID = pm.post_id
+                 WHERE p.post_type = 'product'
+                   AND p.post_status IN ('publish','private','draft')
+                   AND pm.meta_key = '_product_attributes'
+            ";
+            $rows = $wpdb->get_col($sql_parents);
+            $values = array();
+            if (!empty($rows)) {
+                foreach ($rows as $serialized) {
+                    $attrs = maybe_unserialize($serialized);
+                    if (!is_array($attrs) || !isset($attrs[$sanitized_key])) {
+                        continue;
+                    }
+                    $entry = $attrs[$sanitized_key];
+                    if (empty($entry['is_variation'])) {
+                        continue;
+                    }
+                    if ($is_taxonomy) {
+                        // Taxonomy custom values are stored as term IDs in 'value'.
+                        $term_ids = !empty($entry['value']) ? wp_parse_id_list($entry['value']) : array();
+                        if (empty($term_ids)) {
+                            // Or the parent uses all terms — pull them from the taxonomy.
+                            $tax_name = $sanitized_key;
+                            $terms = get_terms(array('taxonomy' => $tax_name, 'hide_empty' => false));
+                            if (!is_wp_error($terms)) {
+                                foreach ($terms as $t) {
+                                    $values[] = $t->slug;
+                                }
+                            }
+                        } else {
+                            foreach ($term_ids as $tid) {
+                                $term = get_term($tid, $sanitized_key);
+                                if ($term && !is_wp_error($term)) {
+                                    $values[] = $term->slug;
+                                }
+                            }
+                        }
+                    } else {
+                        $raw = isset($entry['value']) ? (string) $entry['value'] : '';
+                        if ($raw !== '') {
+                            $parts = array_map('trim', explode('|', $raw));
+                            foreach ($parts as $p) {
+                                if ($p !== '') {
+                                    $values[] = $p;
+                                }
+                            }
+                        }
+                    }
+                }
+                $values = array_values(array_unique($values));
+            }
         }
 
-        $is_taxonomy = (strpos($attribute_key, 'pa_') === 0) || taxonomy_exists($attribute_key);
+        if (empty($values)) {
+            return array();
+        }
 
         $result = array();
         foreach ($values as $value) {