En este tutorial voy a explicar como localizar y listar productos de WooCommerce que contengan otro producto específico almacenado en la base de datos como venta cruzada o dirigida.
En un proyecto de una tienda online con muchos productos normalmente se almacena este tipo de información como son las ventas cruzadas y dirigidas en cada producto de forma individual. Es una buena práctica que también necesita de un buen mantenimiento y actualizaciones.
En ocasiones nos vamos a encontrar con información que se queda huérfana en la base de datos como son productos descatalogados, fuera de stock, sin producción… etc. ¿Qué haces cuando esto sucede?
Pues bien, es complicado gestionar este tipo de situaciones, hay que buscar entre los miles de productos e ir uno a uno cambiando o eliminando estos productos asociados en la sección de ventas dirigidas o cruzadas. Esta tarea puede llevar mucho tiempo.
Para facilitar el trabajo de mis clientes, he desarrollado una solución automática basada en código en la que necesitaré hacer algunas consultas en la base de datos, ya que WooCommerce almacena su relación de ventas cruzadas y dirigidas en la misma.
Crearé un primer snippet
de código que me ayudará a listar los productos que contengan otro específico a través del ID que le indique. Lo introduciré en un shortcode, para poder verlo en una página privada. De esta forma veré de un vistazo cuanto productos son los que tengo que modificar.
El segundo Snippet
me servirá para lanzar una función que modificará la base de datos. De nuevo tendrá que localizar estos productos e indicarle que me haga una sustitución automática de este producto «descatalogado» por otro que ha entrado en la tienda para sustituirlo.
Antes de ejecutar cualquier Snippet, por favor haced copia de seguridad de la base de datos.
¿Qué necesitamos para su desarrollo?
- Acceso a la base de datos de WooCommerce => Accederemos a la base de datos a través de un cliente MySQL.
- Identificar la relación de ventas cruzadas o dirigidas de cada producto => Las ventas cruzadas se almacenan como meta dato del producto dentro de la tabla
wp_postmeta
en el meta_key_crossell_ids
y las ventas dirigidas se almacenan de igual forma en el meta_key_upsell_ids
. - Localizar todos los productos con un producto específico en venta cruzada o dirigida => Usaremos una consulta SQL para encontrar los productos que tienen un ID de producto específico en las ventas cruzadas o dirigidas.
Listar productos que contienen otro producto en ventas cruzadas y dirigidas.
Puedes copiar y pegar este código en el archivo functions.php de tu tema en funcionamiento o dentro de un plugin de funcionalidades. No te olvides de modificarlo y adaptarlo a tus necesidades.
function listar_productos_con_ventas_cruzadas_y_upsells($atts) {
global $wpdb;
// ID del producto objetivo
$producto_objetivo_id = 20998;
// Consulta para obtener todos los productos con ventas cruzadas
$productos_ventas_cruzadas = $wpdb->get_results( "
SELECT post_id, meta_value
FROM {$wpdb->prefix}postmeta
WHERE meta_key = '_crosssell_ids'
" );
// Consulta para obtener todos los productos con ventas dirigidas (upsells)
$productos_upsells = $wpdb->get_results( "
SELECT post_id, meta_value
FROM {$wpdb->prefix}postmeta
WHERE meta_key = '_upsell_ids'
" );
// Iniciar el buffer de salida
ob_start();
echo "<h2>Productos que tienen como ventas cruzadas el producto con ID {$producto_objetivo_id}</h2>";
$encontrados_ventas_cruzadas = false;
// Procesar los productos con ventas cruzadas
if (!empty($productos_ventas_cruzadas)) {
echo "<ul>";
foreach ($productos_ventas_cruzadas as $producto) {
$cross_sell_ids = maybe_unserialize($producto->meta_value);
if (is_array($cross_sell_ids) && in_array($producto_objetivo_id, $cross_sell_ids)) {
$titulo = get_the_title($producto->post_id);
$enlace = get_permalink($producto->post_id);
echo "<li><a href='{$enlace}'>{$titulo}</a> (ID: {$producto->post_id})</li>";
$encontrados_ventas_cruzadas = true;
}
}
echo "</ul>";
}
if (!$encontrados_ventas_cruzadas) {
echo "<p>No se encontraron productos con ventas cruzadas del producto con ID {$producto_objetivo_id}.</p>";
}
echo "<h2>Productos que tienen como ventas dirigidas (upsell) el producto con ID {$producto_objetivo_id}</h2>";
$encontrados_upsells = false;
// Procesar los productos con ventas dirigidas (upsell)
if (!empty($productos_upsells)) {
echo "<ul>";
foreach ($productos_upsells as $producto) {
$upsell_ids = maybe_unserialize($producto->meta_value);
if (is_array($upsell_ids) && in_array($producto_objetivo_id, $upsell_ids)) {
$titulo = get_the_title($producto->post_id);
$enlace = get_permalink($producto->post_id);
echo "<li><a href='{$enlace}'>{$titulo}</a> (ID: {$producto->post_id})</li>";
$encontrados_upsells = true;
}
}
echo "</ul>";
}
if (!$encontrados_upsells) {
echo "<p>No se encontraron productos con ventas dirigidas del producto con ID {$producto_objetivo_id}.</p>";
}
// Obtener el contenido del buffer de salida
return ob_get_clean();
}
// Registrar el shortcode
add_shortcode('listar_productos_ventas_cruzadas_upsells', 'listar_productos_con_ventas_cruzadas_y_upsells');
Desarrollo funciones y temas de WordPress a medida
Si necesitas un desarrollo más personalizado o una funcionalidad para tu negocio o proyecto y no sabes como hacerla. Puedes contratar mis servicios.
Te explico algunos detalles del código…
- Para acceder a la base de datos usaremos la instancia de WordPress
wpdb
. - Guardaremos en la variable
$producto_objetivo_id
el ID del producto que estamos buscando. (Esta variable es la que tienes que modificar para que te funcione el código en tu tienda online). - En las variable
$productos_ventas_cruzadas
guardaremos un array con los IDs obtenidos en la consulta para obtener todos los productos con ventas cruzadas. - En la variable
$productos_upsells
guardaremos un array con los IDs obtenidos en la otra consulta para obtener todos los productos con ventas dirigidas. - Recorreremos los arrays y haremos los distintos listados, incluido la obtención del permalink del producto para facilitar su consulta.
- Finalmente pondremos el
Shortcode
[listar_productos_ventas_cruzadas_upsells]
en una página privada para consultarla internamente. - ¡Importante! no te olvides de poner un buffer de salida de PHP para evitar errores de servidor.
El resultado sería el siguiente, en realidad he acortado este listado porque era mas extenso. A mi cliente le habría costado un buen tiempo modificarlos uno a uno.
Función para reemplazar en la base de datos un producto «descatalogado» por otro en ventas cruzadas y dirigidas
function reemplazar_ventas_cruzadas_y_upsells() {
global $wpdb;
// IDs a reemplazar
$producto_antiguo_id = 20998;
$producto_nuevo_id = 11270;
// 1. Obtener productos con ventas cruzadas que contienen el ID 11270
$productos_ventas_cruzadas = $wpdb->get_results( "
SELECT post_id, meta_value
FROM {$wpdb->prefix}postmeta
WHERE meta_key = '_crosssell_ids'
" );
// 2. Obtener productos con ventas dirigidas (upsells) que contienen el ID 11270
$productos_upsells = $wpdb->get_results( "
SELECT post_id, meta_value
FROM {$wpdb->prefix}postmeta
WHERE meta_key = '_upsell_ids'
" );
// Reemplazar el ID en las ventas cruzadas
foreach ($productos_ventas_cruzadas as $producto) {
$cross_sell_ids = maybe_unserialize($producto->meta_value);
if (is_array($cross_sell_ids) && in_array($producto_antiguo_id, $cross_sell_ids)) {
// Reemplazar el ID antiguo por el nuevo
$new_cross_sell_ids = array_map(function($id) use ($producto_antiguo_id, $producto_nuevo_id) {
return $id == $producto_antiguo_id ? $producto_nuevo_id : $id;
}, $cross_sell_ids);
// Serializar y actualizar en la base de datos
$new_meta_value = maybe_serialize($new_cross_sell_ids);
$wpdb->update(
$wpdb->prefix . 'postmeta',
['meta_value' => $new_meta_value],
['post_id' => $producto->post_id, 'meta_key' => '_crosssell_ids']
);
echo "Producto con ID {$producto->post_id}: Ventas cruzadas actualizadas<br>";
}
}
// Reemplazar el ID en las ventas dirigidas (upsell)
foreach ($productos_upsells as $producto) {
$upsell_ids = maybe_unserialize($producto->meta_value);
if (is_array($upsell_ids) && in_array($producto_antiguo_id, $upsell_ids)) {
// Reemplazar el ID antiguo por el nuevo
$new_upsell_ids = array_map(function($id) use ($producto_antiguo_id, $producto_nuevo_id) {
return $id == $producto_antiguo_id ? $producto_nuevo_id : $id;
}, $upsell_ids);
// Serializar y actualizar en la base de datos
$new_meta_value = maybe_serialize($new_upsell_ids);
$wpdb->update(
$wpdb->prefix . 'postmeta',
['meta_value' => $new_meta_value],
['post_id' => $producto->post_id, 'meta_key' => '_upsell_ids']
);
echo "Producto con ID {$producto->post_id}: Ventas dirigidas (upsell) actualizadas<br>";
}
}
echo "Proceso de actualización completado.";
}
// Puedes ejecutar esta función añadiendo un action hook para probarla:
add_action('admin_init', 'reemplazar_ventas_cruzadas_y_upsells');
Te explico algunos detalles del código…
- Para acceder a la base de datos usaremos la instancia de WordPress
wpdb
. - Guardaremos en la variable
$producto_antiguo_id
el ID del producto que estamos buscando. (Esta variable la tienes que modificar para que te funcione el código en tu tienda online). - Guardaremos en la variable
$producto_nuevo_id
el ID del producto por el que vamos a cambiar. (Esta variable la tienes que modificar para que te funcione el código en tu tienda online). - En las variable
$productos_ventas_cruzadas
guardaremos un array con los IDs obtenidos en la consulta para obtener todos los productos con ventas cruzadas. - En la variable
$productos_upsells
guardaremos un array con los IDs obtenidos en la otra consulta para obtener todos los productos con ventas dirigidas. - Recorreremos los arrays y realizaremos durante la consulta la sustitución en base de datos, utilizando
$wpdb->update
de la instancia de WordPress. - Finalmente ejecutaremos la función dentro del hook
admin_init
, para ejecutarla una vez y desactivar la función para evitar su uso de forma repetitiva. - ¡Importante! no te olvides de poner un buffer de salida de PHP para evitar errores de servidor.
Conclusión
Utiliza estos snippets con cuidado, porque puedes colapsar el servidor haciendo consultas muy grandes o provocar consultas infinitas.
Ten en cuenta que el primer snippet es simplemente informativo, una vez ejecutes la actualización de los datos, deberías comprobar si los cambios se han producidos simplemente jugando con los IDs de los productos.
En cuanto al snippet que se ejecuta en el hook admin_init
, úsalo y no te olvides de eliminarlo, dejarlo comentado o deshabilitado según lo estés haciendo.
Nada más, espero que este tutorial te sirva de ayuda en tu día a día como desarrollador, revisa el código porque se puede aprender mucho para realizar otro tipo de funcionalidades.