Если после импорта CSV или синхронизации из внешней системы у товара появляются одинаковые вариации, проблема обычно не в WooCommerce как таковом, а в том, как вы сопоставляете атрибуты, SKU и идентификаторы вариаций. На практике дубли возникают из-за несовпадения значений атрибутов, повторного создания вариации вместо обновления существующей и отсутствия стабильного ключа для поиска записи.
Ниже разберем, как быстро диагностировать источник дублей, как настроить импорт так, чтобы он обновлял существующие вариации, и как проверить результат без риска сломать каталог.
Как понять, где именно появляются дубли
Сначала нужно определить, на каком этапе создается лишняя вариация: в источнике данных, в импортере или уже при сохранении товара в WooCommerce. Это важно, потому что одинаковая проблема выглядит по-разному в админке и в базе.
Признаки, что дубли создает импорт
- одинаковые вариации появляются после каждого запуска импорта;
- SKU у новых вариаций пустой или повторяется;
- в карточке товара видны одинаковые комбинации атрибутов, но разные ID;
- после ручного удаления дублей они возвращаются при следующей синхронизации.
Что проверить в первую очередь
- есть ли у каждой вариации стабильный внешний идентификатор;
- не меняется ли регистр и формат значений атрибутов, например
Blueиblue; - не создается ли новый товар вместо обновления существующего;
- не импортируются ли одновременно и родительский товар, и вариации без связи между ними.
Если у вас есть доступ к базе, полезно посмотреть, сколько вариаций реально хранится у товара. Для этого можно временно выполнить запрос через wp post list или через SQL в phpMyAdmin. Но безопаснее сначала проверить сам файл импорта: часто проблема уже там.
Почему WooCommerce дублирует вариации
WooCommerce не угадывает, что две записи — это одна и та же вариация. Для него важны совпадение родительского товара, набор атрибутов и способ поиска существующей записи. Если импорт не передает стабильный ключ, система считает каждую строку новой сущностью.
| Подход | Когда подходит | Минус |
|---|---|---|
| Импорт по SKU | SKU у вариаций уникален и не меняется | Не работает, если SKU пустой или генерируется заново |
| Импорт по внешнему ID | Есть ID из ERP, PIM или CSV | Нужно хранить и передавать этот ID стабильно |
| Импорт по атрибутам | Небольшой каталог и строгие значения | Легко получить дубли из-за пробелов, регистра и порядка атрибутов |
Если импорт идет из сторонней системы, лучший вариант — хранить внешний ID вариации в meta-поле и обновлять запись по нему. SKU можно использовать как дополнительный фильтр, но не как единственный источник истины.
Пошаговое решение: обновлять, а не создавать заново
Ниже пример логики, которую можно встроить в свой импорт. Она ищет существующую вариацию по внешнему ID, а если не находит — создает новую. Это базовый сценарий, но он уже убирает большую часть дублей.
<?php
function wpexamples_find_variation_by_external_id( $product_id, $external_id ) {
$query = new WP_Query( array(
'post_type' => 'product_variation',
'post_parent' => $product_id,
'posts_per_page' => 1,
'fields' => 'ids',
'meta_query' => array(
array(
'key' => '_external_variation_id',
'value' => $external_id,
),
),
) );
return ! empty( $query->posts ) ? (int) $query->posts[0] : 0;
}
function wpexamples_upsert_variation( $product_id, $data ) {
$external_id = isset( $data['external_id'] ) ? sanitize_text_field( $data['external_id'] ) : '';
if ( ! $external_id ) {
return new WP_Error( 'missing_external_id', 'Не передан внешний ID вариации' );
}
$variation_id = wpexamples_find_variation_by_external_id( $product_id, $external_id );
if ( $variation_id ) {
$variation = new WC_Product_Variation( $variation_id );
} else {
$variation = new WC_Product_Variation();
$variation->set_parent_id( $product_id );
}
$variation->set_attributes( array(
'pa_color' => sanitize_title( $data['color'] ),
'pa_size' => sanitize_title( $data['size'] ),
) );
if ( isset( $data['sku'] ) ) {
$variation->set_sku( wc_clean( $data['sku'] ) );
}
if ( isset( $data['regular_price'] ) ) {
$variation->set_regular_price( wc_format_decimal( $data['regular_price'] ) );
}
$variation->update_meta_data( '_external_variation_id', $external_id );
$variation->save();
return $variation->get_id();
}Этот код не привязан к конкретному плагину импорта. Его можно использовать в собственном скрипте, в обработчике WP-Cron или в интеграции с внешним API. Главное — не создавать новую вариацию, пока вы не проверили, существует ли уже запись с тем же внешним ID.
Если импорт идет из CSV
В CSV обязательно должен быть столбец с внешним ID вариации. Если его нет, добавьте его на стороне источника данных. Сопоставление только по названию атрибутов — слабое место: достаточно лишнего пробела или другого порядка колонок, и импортер создаст дубль.
Минимальный набор колонок для стабильного импорта:
parent_skuили другой ключ родительского товара;variation_external_id;attribute_pa_color,attribute_pa_sizeи другие атрибуты;skuвариации;regular_priceи, если нужно,sale_price.
Если импорт идет из ERP или API
Внешняя система должна отдавать один и тот же ID для одной и той же вариации. Если ERP пересоздает ID при каждом выгрузочном цикле, WooCommerce не сможет отличить обновление от новой записи. В этом случае лучше сначала стабилизировать источник, а уже потом писать импорт.
Как удалить уже созданные дубли
Если дубли уже накопились, сначала сделайте резервную копию базы. После этого можно удалить лишние вариации вручную, если каталог небольшой, или пройтись скриптом по повторяющимся комбинациям атрибутов и внешнему ID.
Пример простой проверки на дубли по внешнему ID:
<?php
function wpexamples_find_duplicate_variations( $product_id ) {
$query = new WP_Query( array(
'post_type' => 'product_variation',
'post_parent' => $product_id,
'posts_per_page' => -1,
'fields' => 'ids',
) );
$seen = array();
$duplicates = array();
foreach ( $query->posts as $variation_id ) {
$external_id = get_post_meta( $variation_id, '_external_variation_id', true );
if ( ! $external_id ) {
continue;
}
if ( isset( $seen[ $external_id ] ) ) {
$duplicates[] = $variation_id;
continue;
}
$seen[ $external_id ] = $variation_id;
}
return $duplicates;
}Удалять такие записи автоматически стоит только после теста на копии базы. Если у дублей есть заказы, отзывы или связанный остаток, сначала проверьте, не используется ли вариация в истории заказов.
Проверка результата после внедрения
После исправления импорта важно не ограничиваться визуальной проверкой в карточке товара. Дубли могут не отображаться сразу, но появляться при следующем запуске синхронизации.
- Запустите импорт на тестовом товаре с двумя-тремя вариациями.
- Проверьте, что после повторного запуска количество вариаций не выросло.
- Сравните ID вариаций до и после импорта: существующие записи должны обновляться, а не заменяться.
- Откройте товар в админке и убедитесь, что комбинации атрибутов не повторяются.
- Проверьте наличие мета-поля
_external_variation_idу каждой вариации.
Если вы используете WP-CLI или SQL для контроля, полезно сравнить количество вариаций до и после повторного прогона. Повторный импорт не должен увеличивать число записей при неизменных данных.
Частые ошибки и как их исправить
Импорт ищет вариацию по названию атрибута
Это ненадежно. Названия могут отличаться по регистру, локали и пробелам. Используйте внешний ID или хотя бы SKU, если он стабилен.
SKU генерируется заново при каждом импорте
Тогда SKU перестает быть ключом обновления. Исправление простое: SKU должен приходить из источника и не меняться без причины.
Родительский товар создается заново
В этом случае все вариации тоже будут создаваться заново. Проверьте, что у товара есть свой внешний ID и что импорт сначала находит родителя, а уже потом обновляет его вариации.
Атрибуты передаются в разном формате
Например, XL и xl для WooCommerce могут стать разными значениями, если вы не нормализуете данные. Перед сохранением приводите значения к одному формату через sanitize_title() или собственную нормализацию.
Что сделать для безопасности и производительности
Если импорт большой, не запускайте его через обычный веб-запрос без ограничений. Это повышает риск таймаута и частично записанных данных. Лучше разбивать обработку на порции и запускать ее через WP-Cron или отдельный CLI-скрипт, если он у вас уже есть.
- делайте резервную копию базы перед массовым обновлением;
- тестируйте импорт на staging-копии магазина;
- логируйте внешний ID, SKU и ID созданной/обновленной вариации;
- не храните логи с чувствительными данными без необходимости;
- после массовых изменений очищайте кэш страниц и объектный кэш, если он используется.
Если вам нужно регулярно чистить дубли и другие артефакты после импорта, имеет смысл вынести это в отдельную служебную задачу, а не держать в ручном процессе. Для общей чистки сайта и удаления дублей метаданных можно посмотреть Clearfy Pro: https://wpshop.ru/plugins/clearfy.
Главная проверка здесь простая: повторный импорт не должен увеличивать число вариаций у уже существующего товара. Если это условие выполняется, значит, вы действительно исправили логику обновления, а не просто скрыли симптомы в админке.