2

I have this weird situation when I deleted two child taxonomies but they still seem to be assigned to their parent.

I need a overview site for all our products with the taxonomy title ahead. If there is a child taxonomy the products should display under the child, and not under the parent taxonomy.

the code

$args = array(
    'type'                     => 'anco_produkt',
    'orderby'                  => 'menu_order',
    'order'                    => 'ASC',
    'taxonomy'                 => 'produkt_category'
); 
$taxonomies = get_categories($args);

foreach ($taxonomies as $taxonomy) {
  if($taxonomy->parent == 0) {
    echo '<h3 class="alignnone">'.$taxonomy->name.'</h3>';
  } else {
    echo '<h4>'.$taxonomy->name.'</h4>';
  }

  // Get the children of current taxonomy
  $tax_children = get_term_children($taxonomy->term_id , 'produkt_category');

  if(count($tax_children) == 0) { // Are there children?
    // Do something with the posts
  }

It works great!

But I have a taxonomy with two former child-taxonomies. I deleted them and assigned all the affected posts to the parent category, but the posts don't show up under the parent category.

I tested if I get any children with:

print_r($tax_children);
echo count($tax_children);

And voilà, I get their IDs (19 and 22) and a count of 2 children for this "childrenless" parent. In the DB are no taxonomies/categories with these IDs. I tried wp_delete_term() and wp_delete_category(), this parent just can't let go of it's children :((

the problem in action: http://ch-de.ancotech.com/produkte/

the taxonomies the taxonomies I currently use

There should be 15 products under 'Spezialbewehrungen'...

Has someone a idea?

Ps. Should I send the parent to therapy to accept the loss of its children?

3

The problem

There's on (imho) serious issue with WordPress and Taxonomies and their term hierarchy (and children): They aren't really fetched from the actual state, but someone who thought (s)he might be really "smart" stuffed that into the *_options table.

Just take a look at the source of get_term_children(): It makes a call to _get_term_hierarchy(). And in there the following is used to retrieve the children of a taxonomy:

get_option( "{$taxonomy}_children" )

Now it might sound smart at first to leverage a static array instead of a real count, but at the long term it's a maintenance nightmare. WordPress has to keep that value updated and in case someone forgets to trigger that update programmatically, it gets out of sync and fails.

Real world example

To get a proof for that, you can do a plain DB call in (for e.g.) phpMyAdmin (or whatever you use): Just replace {$wpdb->prefix} with your DB table prefix.

SELECT * 
FROM  `{$wpdb->prefix}_options` 
WHERE  `option_name` LIKE  '%children'
ORDER BY  `{$wpdb->prefix}_options`.`option_name` ASC 
LIMIT 0 , 1000

The result will be something like the following - a serialized array:

a:2:{i:23;a:5:{i:0;i:38;i:1;i:39;i:2;i:40;i:3;i:41;i:4;i:42;}i:40;a:1:{i:0;i:43;}}

Or when deserialized:

array (
  23 => 
  array (
    0 => 38,
    1 => 39,
    2 => 40,
    3 => 41,
    4 => 42,
  ),
  40 => 
  array (
    0 => 43,
  ),
)

So the keys are the parent terms, while the sub arrays are the associated child terms.

What's wrong?

First off: I'm not completely sure about this, as WP uses plenty of plain functions and DB calls and tracing all routes is quite tough - maybe I got lost in some rabbit hole.

When I look at wp_delete_term() it seems that there's nowhere an update of the actual option happening. And wp_delete_category() is just a wrapper for that. So in order to keep your children in sync, you'll have to manually update that option.

I've written an importer once, where I stumbled upon the same problem and had to update the option manually as well. So unless no one smarter than me enters the stage, I think this is the only way to go: update_option():

$taxonomy = 'your_current_taxonomy';
$children = get_option( "{$taxonomy}_children" );
// Merge here
update_option( "{$taxonomy}_children", $your_new_value );

EDIT (1)

One more thing for everyone who wants to reproduce that or get some proof: There's no way to actually see that the children are out of synch, unless you take a look at the taxonomy list table screen in admin or comparing the option return value manually. Everything else will just work as expected and look ok.

EDIT (2)

As @MannyFleurmond just commented, there might be another option: Deleting the option and let WP regenerate it for you. I searched core and looked into wp_delete_term() in ~/wp-includes/taxonomy.php... and found something: clean_term_cacheSource really has a part that does this:

    if ( $clean_taxonomy ) {
        wp_cache_delete('all_ids', $taxonomy);
        wp_cache_delete('get', $taxonomy);
        delete_option("{$taxonomy}_children");
        // Regenerate {$taxonomy}_children
        _get_term_hierarchy($taxonomy);
    }

Point still is, that it doesn't trigger. The reason is simple: The 3rd argument is true per default. And therefore - see source - it won't trigger an update.

A possible solution might be the following: Use a hook and clean it manually to trigger an update:

// The last hook before everything happens in core:
do_action( 'deleted_term_taxonomy', $tt_id );

So we could use that:

<?php
defined( 'ABSPATH' ) OR exit;
/** Plugin Name: (#117373) Fix Child term relationships */

add_action( 'deleted_term_taxonomy', 'wpse117374UpdateTaxChildCount' );
function wpse117374UpdateTaxChildCount( $termID )
{
    $tax = get_current_screen()->taxonomy;
    delete_option( "{$tax}_children" );
    # OR: ... alternate solution
    // clear_term_cache( $termID, get_current_screen()->taxonomy, false );
}

Keep in mind that above plugin isn't tested. But if it works, please leave a comment so people can simply use this as mu-plugin to automatically fix this crap :)

  • If you delete the option, WordPress will recreate it the next time it calls for the Taxonomy. Might be an alternate solution. – Manny Fleurmond Oct 10 '13 at 13:38
  • 1
    Ok, updated by @MannyFleurmond recommendation. Seems like this might be a fix. :) Thanks Manny! – kaiser Oct 10 '13 at 13:55
  • 2
    What @MannyFleurmond said is absolutely true. I've deleted all {$taxonomy}_children option using a simple SQL query (and checked db if delete was successful). After that I login, visited Dashboard -> Posts -> Categories and just after page loaded the category_children option appear in db. – gmazzap Oct 10 '13 at 13:58
  • 1
    Got the info from a similar question I asked a while ago: wordpress.stackexchange.com/questions/8357/… . Had the same problem of modifying a taxonomy and it not being reflected on the tax page. – Manny Fleurmond Oct 10 '13 at 14:04
  • Thank you for the detailed information and solution! Because it had to be solved quick I followed @MannyFleurmonds solution by deleting the DB entries. Worked like a charm. When I get the time I will test the plugin in a save test environment and will report back for further use. – lastria Oct 11 '13 at 8:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.