1. Объединение нескольких характеристик в одну
prepare("SELECT id, code, name, type FROM shop_feature WHERE name LIKE ? OR code LIKE ? ORDER BY name"); $stmt->execute(['%' . $search . '%', '%' . $search . '%']); $features = $stmt->fetchAll(); if (!empty($features)): ?>2. Преобразование значений внутри одной характеристики
Приводит значения характеристик к целому числу "до 10", "10 м2", "10.5" → "10"
prepare("SELECT id, code, name FROM shop_feature WHERE name LIKE ? OR code LIKE ? OR id = ? ORDER BY name LIMIT 20"); $stmt->execute(["%$norm_search%", "%$norm_search%", $norm_search]); $norm_features = $stmt->fetchAll(PDO::FETCH_ASSOC); ?>Выберите характеристику:
-
ID = $f['id'] ?> — = htmlspecialchars($f['name']) ?>
(= htmlspecialchars($f['code'] ?: '—') ?>)
Ничего не найдено
ID = $norm_feature['id'] ?> — = htmlspecialchars($norm_feature['name']) ?>
Уникальных значений: = count($norm_values) ?>
| ID значения | Значение |
|---|---|
| = $v['id'] ?> | = htmlspecialchars($v['value']) ?> |
3. Массовое добавление единиц измерения
Добавляет выбранную единицу ко всем значениям (например, "10" → "10 кв. м.")
prepare("SELECT id, code, name FROM shop_feature WHERE name LIKE ? OR code LIKE ? OR id = ? ORDER BY name LIMIT 20"); $stmt->execute(["%$units_search%", "%$units_search%", $units_search]); $found = $stmt->fetchAll(PDO::FETCH_ASSOC); ?>Выберите характеристику:
-
ID = $f['id'] ?> — = htmlspecialchars($f['name']) ?>
(= htmlspecialchars($f['code'] ?: '—') ?>)
Ничего не найдено
ID = $units_feature['id'] ?> — = htmlspecialchars($units_feature['name']) ?>
Значений: = count($units_values) ?>
| ID | Текущее значение |
|---|---|
| = $v['id'] ?> | = htmlspecialchars($v['value']) ?> |
4. Объединение дублей значений внутри характеристики
Ручное слияние похожих значений (например, "да" и "да, опция" → "опция")
prepare("SELECT id, code, name FROM shop_feature WHERE name LIKE ? OR code LIKE ? OR id = ? ORDER BY name LIMIT 20"); $stmt->execute(["%$mv_search%", "%$mv_search%", $mv_search]); $found = $stmt->fetchAll(PDO::FETCH_ASSOC); ?>Выберите характеристику:
-
ID = $f['id'] ?> — = htmlspecialchars($f['name']) ?>
(= htmlspecialchars($f['code'] ?: '—') ?>)
ID = $mv_feature['id'] ?> — = htmlspecialchars($mv_feature['name']) ?>
Уникальных значений: = count($mv_values) ?>
5. Удаление дубликатов товаров
Находит товары с одинаковым названием в выбранной категории и всех её подкатегориях
query("SELECT id, name, depth, left_key, right_key FROM shop_category WHERE status = 1 ORDER BY left_key")->fetchAll(PDO::FETCH_ASSOC); $selected_cat = (int)($_GET['cat_id'] ?? 0); $dup_message = ''; $duplicates = []; if ($selected_cat > 0 && isset($_GET['search'])) { $cat_stmt = $pdo->prepare("SELECT left_key, right_key FROM shop_category WHERE id = ?"); $cat_stmt->execute([$selected_cat]); $cat = $cat_stmt->fetch(PDO::FETCH_ASSOC); if ($cat) { $stmt = $pdo->prepare(" SELECT p.id, p.name, p.price, p.create_datetime FROM shop_product p JOIN shop_category_products cp ON cp.product_id = p.id JOIN shop_category c ON c.id = cp.category_id WHERE c.left_key >= ? AND c.right_key <= ? AND p.status = 1 ORDER BY p.name, p.price ASC, p.create_datetime ASC "); $stmt->execute([$cat['left_key'], $cat['right_key']]); $all_products = $stmt->fetchAll(PDO::FETCH_ASSOC); $groups = []; foreach ($all_products as $prod) { $groups[$prod['name']][] = $prod; } foreach ($groups as $name => $prods) { if (count($prods) > 1) { $duplicates = array_merge($duplicates, $prods); } } } } if (isset($_POST['action']) && $_POST['action'] === 'delete_duplicates' && !empty($_POST['delete_ids'])) { $delete_ids = array_map('intval', $_POST['delete_ids']); $placeholders = str_repeat('?,', count($delete_ids) - 1) . '?'; $pdo->beginTransaction(); try { $pdo->prepare("DELETE FROM shop_product_features WHERE product_id IN ($placeholders)")->execute($delete_ids); $pdo->prepare("DELETE FROM shop_product_images WHERE product_id IN ($placeholders)")->execute($delete_ids); $pdo->prepare("DELETE FROM shop_product_skus WHERE product_id IN ($placeholders)")->execute($delete_ids); $pdo->prepare("DELETE FROM shop_product_pages WHERE product_id IN ($placeholders)")->execute($delete_ids); $pdo->prepare("DELETE FROM shop_product_reviews WHERE product_id IN ($placeholders)")->execute($delete_ids); $pdo->prepare("DELETE FROM shop_product_related WHERE product_id IN ($placeholders) OR related_product_id IN ($placeholders)")->execute(array_merge($delete_ids, $delete_ids)); $pdo->prepare("DELETE FROM shop_category_products WHERE product_id IN ($placeholders)")->execute($delete_ids); $pdo->prepare("DELETE FROM shop_product WHERE id IN ($placeholders)")->execute($delete_ids); $pdo->commit(); $dup_message = "Полностью удалено " . count($delete_ids) . " дубликатов товаров!
Все связанные данные (характеристики, изображения, SKU, отзывы и т.д.) также удалены.
Дубликатов по названию не найдено в этой категории и подкатегориях.
6. Массовое добавление или изменение Meta (title и description) в категориях и товарах.
Поддерживаемые переменные:
Для категорий: {category_name}
Для товаров: {product_name}
Meta для категорий
Meta для товаров
query("SELECT COUNT(*) FROM shop_product"); $total_products = $total_stmt->fetchColumn(); $apply_to_all = ($prod_search_cat == 0 && $prod_search_name === ''); if ($apply_to_all) { } else { $page = max(1, (int)($_GET['page'] ?? 1)); $per_page = 300; $offset = ($page - 1) * $per_page; $sql = "SELECT p.id, p.name, p.price FROM shop_product p"; $params = []; $where = []; if ($prod_search_cat > 0) { $cat_stmt = $pdo->prepare("SELECT left_key, right_key FROM shop_category WHERE id = ?"); $cat_stmt->execute([$prod_search_cat]); $cat = $cat_stmt->fetch(); if ($cat) { $where[] = "EXISTS (SELECT 1 FROM shop_category_products cp JOIN shop_category c ON c.id = cp.category_id WHERE cp.product_id = p.id AND c.left_key >= ? AND c.right_key <= ?)"; $params[] = $cat['left_key']; $params[] = $cat['right_key']; } } if ($prod_search_name !== '') { $where[] = "p.name LIKE ?"; $params[] = '%' . $prod_search_name . '%'; } $where_sql = !empty($where) ? " WHERE " . implode(' AND ', $where) : ''; $count_stmt = $pdo->prepare("SELECT COUNT(*) FROM shop_product p" . $where_sql); $count_stmt->execute($params); $found_count = $count_stmt->fetchColumn(); $stmt = $pdo->prepare($sql . $where_sql . " ORDER BY p.name LIMIT $per_page OFFSET $offset"); $stmt->execute($params); $products = $stmt->fetchAll(PDO::FETCH_ASSOC); $total_pages = ceil($found_count / $per_page); } ?>7. Сортировка значений характеристик
Изменение порядка отображения значений в фильтрах (поле sort)
0) { $stmt = $pdo->prepare("SELECT id, code, name FROM shop_feature WHERE id = ?"); $stmt->execute([$sort_fid]); $sort_feature = $stmt->fetch(PDO::FETCH_ASSOC); if ($sort_feature) { $stmt = $pdo->prepare("SELECT id, value, sort FROM shop_feature_values_varchar WHERE feature_id = ? ORDER BY sort ASC, value ASC"); $stmt->execute([$sort_fid]); $sort_values = $stmt->fetchAll(PDO::FETCH_ASSOC); } } if (isset($_POST['action']) && $_POST['action'] === 'save_sort' && $sort_fid > 0 && !empty($_POST['sort_order'])) { $sort_order = $_POST['sort_order']; $pdo->beginTransaction(); $stmt = $pdo->prepare("UPDATE shop_feature_values_varchar SET sort = ? WHERE id = ?"); foreach ($sort_order as $value_id => $new_sort) { $value_id = (int)$value_id; $new_sort = max(0, (int)$new_sort); $stmt->execute([$new_sort, $value_id]); } $pdo->commit(); $sort_message = "Порядок сохранён в базе!
Выберите атрибут:
-
ID = $f['id'] ?> — = htmlspecialchars($f['name']) ?>
(= htmlspecialchars($f['code'] ?: '—') ?>)
Ничего не найдено
Сортировка: ID = $sort_feature['id'] ?> — = htmlspecialchars($sort_feature['name']) ?>
Значений: = count($sort_values) ?>. Нажмите кнопку — поля заполнятся автоматически. Подправьте при необходимости и сохраните.
8. Перемещение характеристик в группы
Группы — это характеристики с type = 'divider'.
Группа определяется положением divider в списке
внутри выбранного типа товаров, как в админке Shop-Script.
Если у вас нет групп, то их необходимо создать в админке сайта Настройки -> Типы и характеристики товаров -> Заголовок группы характеристик.
= $group_sort_message ?? '' ?> query(" SELECT id, name FROM shop_type ORDER BY name "); $types = $stmt->fetchAll(PDO::FETCH_ASSOC); $current_type_id = isset($_GET['type_id']) ? (int)$_GET['type_id'] : ($types[0]['id'] ?? 0); $stmt = $pdo->query(" SELECT id, name FROM shop_feature WHERE type = 'divider' ORDER BY name "); $groups = $stmt->fetchAll(PDO::FETCH_ASSOC); $group_map = []; foreach ($groups as $g) { $group_map[$g['id']] = $g['name'] . ' (ID ' . $g['id'] . ')'; } $features = []; if ($current_type_id) { $stmt = $pdo->prepare(" SELECT tf.sort, f.id, f.name, f.code, f.type FROM shop_type_features tf JOIN shop_feature f ON f.id = tf.feature_id WHERE tf.type_id = :type_id ORDER BY tf.sort ASC "); $stmt->execute(['type_id' => $current_type_id]); $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); $current_group_id = 0; foreach ($rows as $row) { if ($row['type'] === 'divider') { $current_group_id = $row['id']; $features[] = [ 'id' => $row['id'], 'name' => $row['name'], 'code' => $row['code'], 'type' => $row['type'], 'group_id' => 0 ]; } else { $features[] = [ 'id' => $row['id'], 'name' => $row['name'], 'code' => $row['code'], 'type' => $row['type'], 'group_id' => $current_group_id ]; } } } ?>