|
@@ -447,13 +447,19 @@ class Studiou_WC_Product_Manage_Product_Import {
|
|
|
$product->set_short_description(isset($data['Krátký popis']) ? $data['Krátký popis'] : '');
|
|
$product->set_short_description(isset($data['Krátký popis']) ? $data['Krátký popis'] : '');
|
|
|
$product->set_description(isset($data['Popis']) ? $data['Popis'] : '');
|
|
$product->set_description(isset($data['Popis']) ? $data['Popis'] : '');
|
|
|
|
|
|
|
|
- // Set categories
|
|
|
|
|
|
|
+ // v1.7.1 — M7: gate success-path logging behind WP_DEBUG so a
|
|
|
|
|
+ // normal production import doesn't write dozens of lines per
|
|
|
|
|
+ // row to error.log. Failure-path logs stay always-on (matches
|
|
|
|
|
+ // the documented "Only logs failures" policy).
|
|
|
|
|
+ $debug = defined('WP_DEBUG') && WP_DEBUG;
|
|
|
if (isset($data['Kategorie']) && !empty($data['Kategorie'])) {
|
|
if (isset($data['Kategorie']) && !empty($data['Kategorie'])) {
|
|
|
$categories = array_map('trim', explode('|', $data['Kategorie']));
|
|
$categories = array_map('trim', explode('|', $data['Kategorie']));
|
|
|
$category_ids = array();
|
|
$category_ids = array();
|
|
|
|
|
|
|
|
- error_log('STUDIOU WC IMPORT: Processing categories for product: ' . $product->get_name());
|
|
|
|
|
- error_log('STUDIOU WC IMPORT: Categories string from CSV: ' . $data['Kategorie']);
|
|
|
|
|
|
|
+ if ($debug) {
|
|
|
|
|
+ error_log('STUDIOU WC IMPORT: Processing categories for product: ' . $product->get_name());
|
|
|
|
|
+ error_log('STUDIOU WC IMPORT: Categories string from CSV: ' . $data['Kategorie']);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
foreach ($categories as $category_name) {
|
|
foreach ($categories as $category_name) {
|
|
|
if (empty($category_name)) {
|
|
if (empty($category_name)) {
|
|
@@ -461,22 +467,29 @@ class Studiou_WC_Product_Manage_Product_Import {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
$category_id = $this->find_category_by_name($category_name);
|
|
$category_id = $this->find_category_by_name($category_name);
|
|
|
- error_log('STUDIOU WC IMPORT: Looking for category "' . $category_name . '" - Result: ' . ($category_id ? 'FOUND ID=' . $category_id : 'NOT FOUND'));
|
|
|
|
|
|
|
+ if ($debug) {
|
|
|
|
|
+ error_log('STUDIOU WC IMPORT: Looking for category "' . $category_name . '" - Result: ' . ($category_id ? 'FOUND ID=' . $category_id : 'NOT FOUND'));
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
if ($category_id) {
|
|
if ($category_id) {
|
|
|
$category_ids[] = $category_id;
|
|
$category_ids[] = $category_id;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- error_log('STUDIOU WC IMPORT: Total category IDs found: ' . count($category_ids) . ' - IDs: ' . implode(',', $category_ids));
|
|
|
|
|
|
|
+ if ($debug) {
|
|
|
|
|
+ error_log('STUDIOU WC IMPORT: Total category IDs found: ' . count($category_ids) . ' - IDs: ' . implode(',', $category_ids));
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
if (!empty($category_ids)) {
|
|
if (!empty($category_ids)) {
|
|
|
$product->set_category_ids($category_ids);
|
|
$product->set_category_ids($category_ids);
|
|
|
- error_log('STUDIOU WC IMPORT: Categories assigned to product via set_category_ids()');
|
|
|
|
|
|
|
+ if ($debug) {
|
|
|
|
|
+ error_log('STUDIOU WC IMPORT: Categories assigned to product via set_category_ids()');
|
|
|
|
|
+ }
|
|
|
} else {
|
|
} else {
|
|
|
|
|
+ // Always-on: a row that names categories but resolves zero is a real failure worth logging.
|
|
|
error_log('STUDIOU WC IMPORT: WARNING - No valid category IDs found for: ' . $data['Kategorie']);
|
|
error_log('STUDIOU WC IMPORT: WARNING - No valid category IDs found for: ' . $data['Kategorie']);
|
|
|
}
|
|
}
|
|
|
- } else {
|
|
|
|
|
|
|
+ } else if ($debug) {
|
|
|
error_log('STUDIOU WC IMPORT: No categories in CSV for product: ' . $product->get_name());
|
|
error_log('STUDIOU WC IMPORT: No categories in CSV for product: ' . $product->get_name());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -882,82 +895,113 @@ class Studiou_WC_Product_Manage_Product_Import {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * Find category by name (simple lookup, categories have unique names)
|
|
|
|
|
|
|
+ * v1.7.1 — M8: per-request cache of the entire product_cat term list.
|
|
|
|
|
+ * Populated lazily on first miss-path call to get_terms(); reused for
|
|
|
|
|
+ * every subsequent row in the same AJAX batch. Resets naturally when
|
|
|
|
|
+ * the request ends, so changes mid-import (e.g. categories we just
|
|
|
|
|
+ * created) are picked up — wp_insert_term + cache append is handled
|
|
|
|
|
+ * inline below.
|
|
|
|
|
+ *
|
|
|
|
|
+ * @var array<int,object>|null Terms keyed by term_id, or null if not loaded.
|
|
|
|
|
+ */
|
|
|
|
|
+ private static $all_product_cats_cache = null;
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * v1.7.1 — M8: thin alias of find_or_create_category_by_name() so
|
|
|
|
|
+ * existing call sites keep compiling. The rename clarifies that this
|
|
|
|
|
+ * method also CREATES the term as a side effect, which used to be
|
|
|
|
|
+ * surprising for a function called "find".
|
|
|
*
|
|
*
|
|
|
- * @param string $category_name Category name
|
|
|
|
|
- * @return int|null Category ID or null if not found
|
|
|
|
|
|
|
+ * @param string $category_name
|
|
|
|
|
+ * @return int|null
|
|
|
*/
|
|
*/
|
|
|
private function find_category_by_name($category_name) {
|
|
private function find_category_by_name($category_name) {
|
|
|
- // Trim and normalize the category name
|
|
|
|
|
- $category_name = trim($category_name);
|
|
|
|
|
|
|
+ return $this->find_or_create_category_by_name($category_name);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Find a category by name; create it if missing.
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param string $category_name
|
|
|
|
|
+ * @return int|null Term ID or null if creation failed.
|
|
|
|
|
+ */
|
|
|
|
|
+ private function find_or_create_category_by_name($category_name) {
|
|
|
|
|
+ $debug = defined('WP_DEBUG') && WP_DEBUG;
|
|
|
|
|
|
|
|
|
|
+ $category_name = trim($category_name);
|
|
|
if (empty($category_name)) {
|
|
if (empty($category_name)) {
|
|
|
return null;
|
|
return null;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // Simple name lookup only (no hierarchy detection)
|
|
|
|
|
- // Categories have unique names throughout the system
|
|
|
|
|
-
|
|
|
|
|
- error_log('STUDIOU WC IMPORT: find_category_by_name() called for: "' . $category_name . '"');
|
|
|
|
|
|
|
+ if ($debug) {
|
|
|
|
|
+ error_log('STUDIOU WC IMPORT: find_or_create_category_by_name() called for: "' . $category_name . '"');
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
// Method 1: Exact name match
|
|
// Method 1: Exact name match
|
|
|
$term = get_term_by('name', $category_name, 'product_cat');
|
|
$term = get_term_by('name', $category_name, 'product_cat');
|
|
|
if ($term) {
|
|
if ($term) {
|
|
|
- error_log('STUDIOU WC IMPORT: Found by exact name match - ID: ' . $term->term_id);
|
|
|
|
|
|
|
+ if ($debug) { error_log('STUDIOU WC IMPORT: Found by exact name match - ID: ' . $term->term_id); }
|
|
|
return $term->term_id;
|
|
return $term->term_id;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Method 2: Try by slug
|
|
// Method 2: Try by slug
|
|
|
$slug = sanitize_title($category_name);
|
|
$slug = sanitize_title($category_name);
|
|
|
- error_log('STUDIOU WC IMPORT: Exact name failed, trying slug: "' . $slug . '"');
|
|
|
|
|
|
|
+ if ($debug) { error_log('STUDIOU WC IMPORT: Exact name failed, trying slug: "' . $slug . '"'); }
|
|
|
$term = get_term_by('slug', $slug, 'product_cat');
|
|
$term = get_term_by('slug', $slug, 'product_cat');
|
|
|
if ($term) {
|
|
if ($term) {
|
|
|
- error_log('STUDIOU WC IMPORT: Found by slug match - ID: ' . $term->term_id);
|
|
|
|
|
|
|
+ if ($debug) { error_log('STUDIOU WC IMPORT: Found by slug match - ID: ' . $term->term_id); }
|
|
|
return $term->term_id;
|
|
return $term->term_id;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // Method 3: Search case-insensitively through all categories
|
|
|
|
|
- $all_terms = get_terms(array(
|
|
|
|
|
- 'taxonomy' => 'product_cat',
|
|
|
|
|
- 'hide_empty' => false
|
|
|
|
|
- ));
|
|
|
|
|
|
|
+ // Method 3: Search case-insensitively through all categories. Pre-1.7.1
|
|
|
|
|
+ // this called get_terms() on EVERY miss-path row — O(N) categories per
|
|
|
|
|
+ // miss, O(N*M) overall on a multi-row import. Now we load once per
|
|
|
|
|
+ // request and reuse.
|
|
|
|
|
+ if (self::$all_product_cats_cache === null) {
|
|
|
|
|
+ $loaded = get_terms(array(
|
|
|
|
|
+ 'taxonomy' => 'product_cat',
|
|
|
|
|
+ 'hide_empty' => false,
|
|
|
|
|
+ ));
|
|
|
|
|
+ self::$all_product_cats_cache = is_array($loaded) ? $loaded : array();
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- error_log('STUDIOU WC IMPORT: Slug failed, searching ' . count($all_terms) . ' categories case-insensitively');
|
|
|
|
|
|
|
+ if ($debug) {
|
|
|
|
|
+ error_log('STUDIOU WC IMPORT: Slug failed, searching ' . count(self::$all_product_cats_cache) . ' categories case-insensitively');
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- foreach ($all_terms as $t) {
|
|
|
|
|
|
|
+ foreach (self::$all_product_cats_cache as $t) {
|
|
|
if (strcasecmp($t->name, $category_name) === 0) {
|
|
if (strcasecmp($t->name, $category_name) === 0) {
|
|
|
- error_log('STUDIOU WC IMPORT: Found by case-insensitive match - ID: ' . $t->term_id . ', name: "' . $t->name . '"');
|
|
|
|
|
|
|
+ if ($debug) { error_log('STUDIOU WC IMPORT: Found by case-insensitive match - ID: ' . $t->term_id . ', name: "' . $t->name . '"'); }
|
|
|
return $t->term_id;
|
|
return $t->term_id;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // Not found - try to create it
|
|
|
|
|
|
|
+ // Not found — create it (always-on log because category creation is a real side effect).
|
|
|
error_log('STUDIOU WC IMPORT: Category NOT FOUND: "' . $category_name . '" - Attempting to create it');
|
|
error_log('STUDIOU WC IMPORT: Category NOT FOUND: "' . $category_name . '" - Attempting to create it');
|
|
|
-
|
|
|
|
|
- $new_term = wp_insert_term(
|
|
|
|
|
- $category_name,
|
|
|
|
|
- 'product_cat',
|
|
|
|
|
- array(
|
|
|
|
|
- 'slug' => $slug
|
|
|
|
|
- )
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
|
|
+ $new_term = wp_insert_term($category_name, 'product_cat', array('slug' => $slug));
|
|
|
if (is_wp_error($new_term)) {
|
|
if (is_wp_error($new_term)) {
|
|
|
error_log('STUDIOU WC IMPORT: Failed to create category "' . $category_name . '": ' . $new_term->get_error_message());
|
|
error_log('STUDIOU WC IMPORT: Failed to create category "' . $category_name . '": ' . $new_term->get_error_message());
|
|
|
- error_log('STUDIOU WC IMPORT: Total available categories: ' . count($all_terms));
|
|
|
|
|
-
|
|
|
|
|
- // Show first 20 categories for debugging
|
|
|
|
|
- $sample_cats = array_slice($all_terms, 0, 20);
|
|
|
|
|
- foreach ($sample_cats as $cat) {
|
|
|
|
|
- error_log('STUDIOU WC IMPORT: Available: "' . $cat->name . '" (ID: ' . $cat->term_id . ', slug: ' . $cat->slug . ')');
|
|
|
|
|
|
|
+ if ($debug) {
|
|
|
|
|
+ error_log('STUDIOU WC IMPORT: Total available categories: ' . count(self::$all_product_cats_cache));
|
|
|
|
|
+ $sample_cats = array_slice(self::$all_product_cats_cache, 0, 20);
|
|
|
|
|
+ foreach ($sample_cats as $cat) {
|
|
|
|
|
+ error_log('STUDIOU WC IMPORT: Available: "' . $cat->name . '" (ID: ' . $cat->term_id . ', slug: ' . $cat->slug . ')');
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
return null;
|
|
return null;
|
|
|
- } else {
|
|
|
|
|
- $term_id = $new_term['term_id'];
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $term_id = (int) $new_term['term_id'];
|
|
|
|
|
+ // Append the new term to the cache so subsequent rows see it
|
|
|
|
|
+ // without re-running get_terms().
|
|
|
|
|
+ $fresh = get_term($term_id, 'product_cat');
|
|
|
|
|
+ if ($fresh && !is_wp_error($fresh)) {
|
|
|
|
|
+ self::$all_product_cats_cache[] = $fresh;
|
|
|
|
|
+ }
|
|
|
|
|
+ if ($debug) {
|
|
|
error_log('STUDIOU WC IMPORT: Successfully created category "' . $category_name . '" with ID: ' . $term_id);
|
|
error_log('STUDIOU WC IMPORT: Successfully created category "' . $category_name . '" with ID: ' . $term_id);
|
|
|
- return $term_id;
|
|
|
|
|
}
|
|
}
|
|
|
|
|
+ return $term_id;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|