Cómo importar productos masivamente en Woocommerce de forma eficiente

  • Autor Autor tiker
  • Fecha de inicio Fecha de inicio
T

tiker

Curioso
Verificado por Whatsapp
¡Usuario con pocos negocios! ¡Utiliza siempre saldo de Forobeta!
Hola, alguien sabe cómo importar productos masivos en woocommerce lo más rápido posible? Normalmente me tarda para 8000 productos 5 horas! Gracias
 
Yo uso el importador nativo para las correcciones de precios con un csv, con 1.000-1.500 productos y el tiempo de actualización es inferior a 1minuto.
Si detallas más, te digo si puedo ayudarte u otro forero sabe.
 
Puedes hacerlo con una funcion, instala en tu plugind de code snippets:

<?php
// Agregar menú en el panel de administración
add_action('admin_menu', 'agregar_menu_importacion');
function agregar_menu_importacion() {
add_menu_page(
'Importar Productos',
'Importar Productos',
'manage_options',
'importador-productos',
'mostrar_pagina_importacion',
'dashicons-upload',
56
);
}

// Crear la página de importación
function mostrar_pagina_importacion() {
// Verificar permisos
if (!current_user_can('manage_options')) {
wp_die('No tienes permisos para acceder a esta página.');
}

// Procesar el formulario si se ha enviado
if (isset($_POST['submit']) && isset($_FILES['archivo_csv'])) {
if (!wp_verify_nonce($_POST['importacion_nonce'], 'importacion_productos')) {
wp_die('Error de seguridad');
}

$archivo = $_FILES['archivo_csv'];
$upload_dir = wp_upload_dir();
$archivo_temporal = $upload_dir['path'] . '/' . basename($archivo['name']);

if (move_uploaded_file($archivo['tmp_name'], $archivo_temporal)) {
$resultados = importar_productos_woocommerce($archivo_temporal);
unlink($archivo_temporal); // Eliminar archivo temporal después de la importación
}
}
?>
<div class="wrap">
<h1>Importador de Productos para WooCommerce</h1>

<div class="notice notice-info">
<p>El archivo CSV debe contener las siguientes columnas: nombre, precio, sku, descripcion, stock, categorias</p>
<p>Las categorías deben estar separadas por el símbolo | (pipe)</p>
</div>

<form method="post" enctype="multipart/form-data">
<?php wp_nonce_field('importacion_productos', 'importacion_nonce'); ?>
<table class="form-table">
<tr>
<th scope="row"><label for="archivo_csv">Seleccionar archivo CSV</label></th>
<td>
<input type="file" name="archivo_csv" id="archivo_csv" accept=".csv" required>
</td>
</tr>
</table>
<?php submit_button('Importar Productos'); ?>
</form>

<?php
// Mostrar resultados si existen
if (isset($resultados)) {
if (is_wp_error($resultados)) {
echo '<div class="notice notice-error"><p>' . esc_html($resultados->get_error_message()) . '</p></div>';
} else {
echo '<div class="notice notice-success"><p>';
echo 'Importación completada:<br>';
echo 'Productos importados: ' . esc_html($resultados['exito']) . '<br>';
echo 'Errores: ' . esc_html($resultados['errores']) . '<br>';
if (!empty($resultados['mensajes'])) {
echo 'Mensajes de error:<br>';
foreach ($resultados['mensajes'] as $mensaje) {
echo '- ' . esc_html($mensaje) . '<br>';
}
}
echo '</p></div>';
}
}
?>
</div>
<?php
}

// Función para validar el formato del CSV
function validar_formato_csv($archivo) {
$required_headers = array('nombre', 'precio', 'sku', 'descripcion', 'stock', 'categorias');
$handle = fopen($archivo, 'r');
$headers = fgetcsv($handle);
fclose($handle);

$missing_headers = array_diff($required_headers, array_map('strtolower', $headers));

if (!empty($missing_headers)) {
return new WP_Error(
'formato_invalido',
'Faltan las siguientes columnas requeridas: ' . implode(', ', $missing_headers)
);
}

return true;
}

// Función principal de importación
function importar_productos_woocommerce($archivo_csv) {
// Verificar que WooCommerce está activo
if (!class_exists('WC_Product')) {
return new WP_Error('error', 'WooCommerce debe estar instalado y activado');
}

// Validar formato
$validacion = validar_formato_csv($archivo_csv);
if (is_wp_error($validacion)) {
return $validacion;
}

// Abrir archivo CSV
$handle = fopen($archivo_csv, 'r');
if (!$handle) {
return new WP_Error('error', 'No se pudo abrir el archivo CSV');
}

// Leer encabezados
$headers = fgetcsv($handle);

// Array para almacenar resultados
$resultados = array(
'exito' => 0,
'errores' => 0,
'mensajes' => array()
);

// Procesar cada línea
while (($data = fgetcsv($handle)) !== FALSE) {
try {
$producto = array_combine($headers, $data);

// Crear nuevo producto
$new_product = new WC_Product_Simple();

// Configurar datos básicos
$new_product->set_name(sanitize_text_field($producto['nombre']));
$new_product->set_regular_price(floatval($producto['precio']));
$new_product->set_sku(sanitize_text_field($producto['sku']));
$new_product->set_description(wp_kses_post($producto['descripcion']));
$new_product->set_stock_quantity(intval($producto['stock']));
$new_product->set_manage_stock(true);

// Categorías si existen
if (!empty($producto['categorias'])) {
$categorias = explode('|', $producto['categorias']);
$cat_ids = array();
foreach ($categorias as $categoria) {
$term = term_exists(trim($categoria), 'product_cat');
if (!$term) {
$term = wp_insert_term(trim($categoria), 'product_cat');
}
if (!is_wp_error($term)) {
$cat_ids[] = is_array($term) ? $term['term_id'] : $term;
}
}
if (!empty($cat_ids)) {
$new_product->set_category_ids($cat_ids);
}
}

// Guardar producto
$new_product->save();
$resultados['exito']++;

} catch (Exception $e) {
$resultados['errores']++;
$resultados['mensajes'][] = "Error en línea " . ($resultados['exito'] + $resultados['errores']) . ": " . $e->getMessage();
}
}

fclose($handle);
return $resultados;
}
 
Algo así me pasaba a mi con un cliente, importando productos nuevos o actualizando existencias, llegaba un momento en que se tardaba horas o simplemente dejaba de funcionar.

Lo que hice fue dividir el .CSV en partes más pequeñas, máximo de 3500 productos, pero aun así tarda un rato, como 50 minutos por lote.

Yo uso el importador nativo para las correcciones de precios con un csv, con 1.000-1.500 productos y el tiempo de actualización es inferior a 1minuto.
Si detallas más, te digo si puedo ayudarte u otro forero sabe.
La próxima vez haré eso, los dividiré en lotes de máximo 1500 para ver si es más rápido.

Puedes hacerlo con una funcion, instala en tu plugind de code snippets:

<?php
// Agregar menú en el panel de administración
add_action('admin_menu', 'agregar_menu_importacion');
function agregar_menu_importacion() {
add_menu_page(
'Importar Productos',
'Importar Productos',
'manage_options',
'importador-productos',
'mostrar_pagina_importacion',
'dashicons-upload',
56
);
}

// Crear la página de importación
function mostrar_pagina_importacion() {
// Verificar permisos
if (!current_user_can('manage_options')) {
wp_die('No tienes permisos para acceder a esta página.');
}

// Procesar el formulario si se ha enviado
if (isset($_POST['submit']) && isset($_FILES['archivo_csv'])) {
if (!wp_verify_nonce($_POST['importacion_nonce'], 'importacion_productos')) {
wp_die('Error de seguridad');
}

$archivo = $_FILES['archivo_csv'];
$upload_dir = wp_upload_dir();
$archivo_temporal = $upload_dir['path'] . '/' . basename($archivo['name']);

if (move_uploaded_file($archivo['tmp_name'], $archivo_temporal)) {
$resultados = importar_productos_woocommerce($archivo_temporal);
unlink($archivo_temporal); // Eliminar archivo temporal después de la importación
}
}
?>
<div class="wrap">
<h1>Importador de Productos para WooCommerce</h1>

<div class="notice notice-info">
<p>El archivo CSV debe contener las siguientes columnas: nombre, precio, sku, descripcion, stock, categorias</p>
<p>Las categorías deben estar separadas por el símbolo | (pipe)</p>
</div>

<form method="post" enctype="multipart/form-data">
<?php wp_nonce_field('importacion_productos', 'importacion_nonce'); ?>
<table class="form-table">
<tr>
<th scope="row"><label for="archivo_csv">Seleccionar archivo CSV</label></th>
<td>
<input type="file" name="archivo_csv" id="archivo_csv" accept=".csv" required>
</td>
</tr>
</table>
<?php submit_button('Importar Productos'); ?>
</form>

<?php
// Mostrar resultados si existen
if (isset($resultados)) {
if (is_wp_error($resultados)) {
echo '<div class="notice notice-error"><p>' . esc_html($resultados->get_error_message()) . '</p></div>';
} else {
echo '<div class="notice notice-success"><p>';
echo 'Importación completada:<br>';
echo 'Productos importados: ' . esc_html($resultados['exito']) . '<br>';
echo 'Errores: ' . esc_html($resultados['errores']) . '<br>';
if (!empty($resultados['mensajes'])) {
echo 'Mensajes de error:<br>';
foreach ($resultados['mensajes'] as $mensaje) {
echo '- ' . esc_html($mensaje) . '<br>';
}
}
echo '</p></div>';
}
}
?>
</div>
<?php
}

// Función para validar el formato del CSV
function validar_formato_csv($archivo) {
$required_headers = array('nombre', 'precio', 'sku', 'descripcion', 'stock', 'categorias');
$handle = fopen($archivo, 'r');
$headers = fgetcsv($handle);
fclose($handle);

$missing_headers = array_diff($required_headers, array_map('strtolower', $headers));

if (!empty($missing_headers)) {
return new WP_Error(
'formato_invalido',
'Faltan las siguientes columnas requeridas: ' . implode(', ', $missing_headers)
);
}

return true;
}

// Función principal de importación
function importar_productos_woocommerce($archivo_csv) {
// Verificar que WooCommerce está activo
if (!class_exists('WC_Product')) {
return new WP_Error('error', 'WooCommerce debe estar instalado y activado');
}

// Validar formato
$validacion = validar_formato_csv($archivo_csv);
if (is_wp_error($validacion)) {
return $validacion;
}

// Abrir archivo CSV
$handle = fopen($archivo_csv, 'r');
if (!$handle) {
return new WP_Error('error', 'No se pudo abrir el archivo CSV');
}

// Leer encabezados
$headers = fgetcsv($handle);

// Array para almacenar resultados
$resultados = array(
'exito' => 0,
'errores' => 0,
'mensajes' => array()
);

// Procesar cada línea
while (($data = fgetcsv($handle)) !== FALSE) {
try {
$producto = array_combine($headers, $data);

// Crear nuevo producto
$new_product = new WC_Product_Simple();

// Configurar datos básicos
$new_product->set_name(sanitize_text_field($producto['nombre']));
$new_product->set_regular_price(floatval($producto['precio']));
$new_product->set_sku(sanitize_text_field($producto['sku']));
$new_product->set_description(wp_kses_post($producto['descripcion']));
$new_product->set_stock_quantity(intval($producto['stock']));
$new_product->set_manage_stock(true);

// Categorías si existen
if (!empty($producto['categorias'])) {
$categorias = explode('|', $producto['categorias']);
$cat_ids = array();
foreach ($categorias as $categoria) {
$term = term_exists(trim($categoria), 'product_cat');
if (!$term) {
$term = wp_insert_term(trim($categoria), 'product_cat');
}
if (!is_wp_error($term)) {
$cat_ids[] = is_array($term) ? $term['term_id'] : $term;
}
}
if (!empty($cat_ids)) {
$new_product->set_category_ids($cat_ids);
}
}

// Guardar producto
$new_product->save();
$resultados['exito']++;

} catch (Exception $e) {
$resultados['errores']++;
$resultados['mensajes'][] = "Error en línea " . ($resultados['exito'] + $resultados['errores']) . ": " . $e->getMessage();
}
}

fclose($handle);
return $resultados;
}
Interesante, trataré de implementarlo cuando deba importar productos masivamente nuevamente.
 
  1. Añade el código en:
    • Functions.php de tu tema hijo (recomendado)
    • Un plugin de snippets como "Code Snippets"


<?php
/**
* Evitar mostrar productos de subcategorías en categoría principal
*/
// Método 1: Usando pre_get_posts (recomendado)
function excluir_productos_subcategorias($query) {
if (!is_admin() && $query->is_main_query() && is_product_category()) {
$query->set('tax_query', array(
array(
'taxonomy' => 'product_cat',
'field' => 'id',
'terms' => get_queried_object_id(),
'include_children' => false
)
));
}
}
add_action('pre_get_posts', 'excluir_productos_subcategorias');
// Método 2: Modificando los argumentos de WooCommerce (alternativa)
function modificar_args_loop_shop($args) {
if (is_product_category()) {
$args['tax_query'] = array(
array(
'taxonomy' => 'product_cat',
'field' => 'id',
'terms' => get_queried_object_id(),
'include_children' => false
)
);
}
return $args;
}
add_filter('woocommerce_product_query_tax_query', 'modificar_args_loop_shop');
// Método 3: Solución específica para widgets (opcional)
function modificar_args_widget_productos($args) {
if (isset($args['tax_query'])) {
foreach ($args['tax_query'] as $key => $value) {
if (isset($value['taxonomy']) && $value['taxonomy'] === 'product_cat') {
$args['tax_query'][$key]['include_children'] = false;
}
}
}
return $args;
}
add_filter('woocommerce_products_widget_query_args', 'modificar_args_widget_productos');
// Método 4: Solución para shortcodes de productos (opcional)
function modificar_args_shortcode_productos($args) {
if (isset($args['category'])) {
$args['tax_query'] = array(
array(
'taxonomy' => 'product_cat',
'field' => 'slug',
'terms' => explode(',', $args['category']),
'include_children' => false
)
);
unset($args['category']);
}
return $args;
}
add_filter('woocommerce_shortcode_products_query', 'modificar_args_shortcode_productos');
 
Atrás
Arriba