Woocommerce: How to Bulk Delete Duplicate Variations within a Product
Let’s say you’ve got a product that is supposed to have three variations, maybe:
- Red Shirt
- Blue Overalls
- Brown Shoes
Perhaps you’ve used a plugin, Woocommerce’s import script, or whatever to add these variations to all 1000 of your site’s products. Perhaps that left you with duplicates though, so every product has all of these variations:
- Red Shirt
- Red Shirt
- Blue Overalls
- Blue Overalls
- Brown Shoes
- Brown Shoes
That’s just cluttering up your database and going to make things tricky down the line. So now, you want to clean it up.
The problem is, plugins that are created to remove duplicate posts, even those that handle custom post types, don’t typically show you the post type that variations are: product_variation
Below are two ways we can make this happen, the first is for folks comfortable with adding code to their theme, the second utilizes a plugin but we need to modify it a bit (and can do so directly from within a browser like Chrome, so no real coding necessary.)
How to Delete Duplicate Variations via PHP Code
Back up your database before you do this! You could easily lose tons of data if you screw up even one little line, or even by using what I have exactly. Just back it up, already!
Go into your theme and open up the file page.php
Find the_content();
or really any part of the code that you can recognize. This is where we’re going to put our code, so we can load up a specific page and make this all happen, and watch it do its job in real time.
Now that you’ve got that, go into WordPress and go to Pages > Add New. Give your new page a name and click Save Draft. In your browser’s address bar, you should now have a URL that resembles this:
https://clicknathan.com/wp-admin/post.php?post=6199&action=edit
We’re concerned with that number, in my case it’s 6199
. Yours will be different. This is the page’s ID.
Back in your page.php file, add this code, and be sure to change the highlighted 6199 part to whatever your page’s ID is (ie, what we just went over in the last paragraph):
<?php if (is_page('6199')) {
$current_list = array();
$args = array(
'posts_per_page' => 500,
'post_type' => 'product_variation',
'orderby' => 'title',
'order' => 'asc',
'offset' => 0
);
$the_query = new WP_Query( $args );
if ($the_query->have_posts()) {
while ( $the_query->have_posts() ) :
$the_query->the_post(); ?>
<?php the_title(); ?><br>
<?php $this_variation_title = get_the_title();
if (in_array($this_variation_title, $current_list)) {
echo $this_variation_title.' duplicate deleted.<br>';
wp_delete_post(get_the_ID());
} else {
array_push($current_list, $this_variation_title);
echo 'Variation added to array.<br>';
} ?>
<?php endwhile; ?>
<?php } ?>
<?php wp_reset_postdata();
} ?>
Let’s walk through what each piece of this code does.
<?php if (is_page('6199')) {
Here we simply say, “If we’re on Page 6199, run this code.” We only want to run it on this page, because we don’t want people executing the code just by visiting any page on our website.
$current_list = array();
Here we create an array, which is basically like a box we can store a bunch of stuff in. In this case, the stuff we store in the box is called our variables. We’ll add these variables to this array later in the code.
$args = array(
'posts_per_page' => 500,
'post_type' => 'product_variation',
'orderby' => 'title',
'order' => 'asc',
'offset' => 0
);
Next, we’re setting up the arguments (ie, $args
) for our WP Query. A WP Query is more or less the backbone of how WordPress displays content, and more specifically how it knows what specific content to pull from the database for us to show on the front end of the site.
Here we say, “Tell WordPress that when it’s time to grab content from the site, we want to get 500 posts from the post type called product_variation (Variations in Woocommerce), we want them ordered by Title (since Variations will all have their parent product’s title), order them ascending and offset them by 0, which means we want to start at the first item in the possible list of results.”
If you have less than 500 products, you’re all set. If you have slightly more (and a good server), you could change the second line there to 'posts_per_page' => -1,
– this -1 tells WordPress to just go ahead and grab all results that match, instead of limiting it to just 500. You can also change this number to something lower, like 100 or 20 if your server is really hanging up and you’re seeing errors.
$the_query = new WP_Query( $args );
if ($the_query->have_posts()) {
while ( $the_query->have_posts() ) :
$the_query->the_post(); ?>
This takes our arguments and actually runs the code to go and get the results.
<?php the_title(); ?><br>
<?php $this_variation_title = get_the_title();
This part will show us the title of the variation, which will be something like Your Product Name – Red Shirt, and then the second line stores the title in a variable called $this_variation_title
. It’s handy that the variation attributes are actually stored in the product’s name, which makes the next bit possible:
if (in_array($this_variation_title, $current_list)) {
echo $this_variation_title.' duplicate deleted.<br>';
wp_delete_post(get_the_ID());
} else {
array_push($current_list, $this_variation_title);
echo 'Variation added to array.<br>';
} ?>
This is where we do the real work. The first line says, “If the title of the variation (which we’re storing in $this_variation_title
) is in our array we called $current_list
, tell us we’ve deleted it and then go ahead and delete it (via wp_delete_post()
).”
As of the fourth line, we say, “If none of that happened though, because the title of the post wasn’t already in our array, then add that title to the array and tell us you did so.”
So we basically check to see if a variation’s title is already in the array, if it is then we delete this occurrence and if not, we add it to the array so that the next time it shows up, we’ll know we should delete it.
Note that this only happens each time you refresh the page, it’s not stored in a database anywhere or anything like that.
All of the code after that just closes up our WP Query, but it is definitely necessary.
Now, if you’ve used 'posts_per_page' => 500,
and 'offset' => 0
like in my original example, but have more than 500 variations, then you need to update this code each time you run the page. For example, you’d change the offset by the same number as the posts_per_page, so in this case, on our second pageload we’d use 'offset' => 500
, on our third we’d use 'offset' => 1000
, on our fourth 'offset' => 1500
and so on. Just always increment by the same number as you’re using in the posts_per_page setting.
Keep going until you don’t have any duplicates left, ie you never see that “VARIATION NAME duplicate deleted.” message again.
Use a Plugin (slightly modified) to Delete Duplicate Woocommerce Variations in Products
There’s a plugin out there by the name of Delete Duplicate Posts. This seems to do a good job at bulk deleting duplicates, however, it doesn’t work with product variations (it works fine with normal products) right out of the box. Luckily, we can easily manipulate it with a browser like Chrome.
Install the plugin and activate it, then go to its settings page which is under Tools > Delete Duplicate Posts.
That page will show you a list of post types on your site, but fails to show the post type product_variation
, likely due to how it’s registered via Woocommerce. Here’s the list if you have a relatively basic WordPress site with Woocommerce installed:
Now, put your cursor over the “product” checkbox, and right click, then choose Inspect.
You’ll now see this in your browser window:
Note this line:
<input type="checkbox" name="ddp_pts[]" id="ddp_pt-3" value="product">
This is all we need to change. Where it says value="product"
, click on the word “product” and you can edit it. Change it so it reads value="product_variation"
That’s all, you can close that new inspector portion of the browser window and scroll down to Save Settings. Click that and you’ll get a list of all duplicate product variations. Note that if you click Save Settings and need to do this again, you have to do the whole Right click, choose Inspect and edit “product” to “product_variation” again, as this is just temporarily modifying the code.
That’s it, enjoy, and I hope you backed things up because even if they didn’t go wrong this time, Murphy’s Law and all, y’all!