Make WordPress Core

Changeset 30241


Ignore:
Timestamp:
11/05/2014 02:02:48 AM (10 years ago)
Author:
boonebgorges
Message:

Split shared taxonomy terms during term update.

When updating an existing taxonomy term that shares its term_id with
another term, we generate a new row in wp_terms and associate the updated
term_taxonomy_id with the new term. This separates the terms, such that
updating the name of one term does not change the name of any others.

Note that this term splitting only occurs on installations whose database
schemas have been upgraded to version 30133 or higher. Note also that shared
terms are only split when run through wp_update_term(), as on edit-tags.php;
we will wait until a future release of WordPress to force the splitting of all
shared taxonomy terms.

Props boonebgorges, rmccue, greuben, garyc40, wonderboymusic, imath, jesin.
Fixes #5809.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/taxonomy.php

    r30240 r30241  
    33863386    }
    33873387
     3388    $tt_id = $wpdb->get_var( $wpdb->prepare( "SELECT tt.term_taxonomy_id FROM $wpdb->term_taxonomy AS tt INNER JOIN $wpdb->terms AS t ON tt.term_id = t.term_id WHERE tt.taxonomy = %s AND t.term_id = %d", $taxonomy, $term_id) );
     3389
     3390    // Check whether this is a shared term that needs splitting.
     3391    $_term_id = _split_shared_term( $term_id, $tt_id );
     3392    if ( ! is_wp_error( $_term_id ) ) {
     3393        $term_id = $_term_id;
     3394    }
     3395
    33883396    /**
    33893397     * Fires immediately before the given terms are edited.
     
    34103418     */
    34113419    do_action( 'edited_terms', $term_id, $taxonomy );
    3412 
    3413     $tt_id = $wpdb->get_var( $wpdb->prepare( "SELECT tt.term_taxonomy_id FROM $wpdb->term_taxonomy AS tt INNER JOIN $wpdb->terms AS t ON tt.term_id = t.term_id WHERE tt.taxonomy = %s AND t.term_id = %d", $taxonomy, $term_id) );
    34143420
    34153421    /**
     
    40394045        do_action( 'edited_term_taxonomy', $term, $taxonomy );
    40404046    }
     4047}
     4048
     4049/**
     4050 * Create a new term for a term_taxonomy item that currently shares its term.
     4051 *
     4052 * @since 4.1.0
     4053 * @access private
     4054 *
     4055 * @param int   $term_id          ID of the shared term.
     4056 * @param int   $term_taxonomy_id ID of the term taxonomy item to receive a new term.
     4057 * @param array $shared_tts       Sibling term taxonomies, used for busting caches.
     4058 * @return int  Term ID.
     4059 */
     4060function _split_shared_term( $term_id, $term_taxonomy_id ) {
     4061    global $wpdb;
     4062
     4063    // Don't try to split terms if database schema does not support shared slugs.
     4064    $current_db_version = get_option( 'db_version' );
     4065    if ( $current_db_version < 30133 ) {
     4066        return $term_id;
     4067    }
     4068
     4069    // If there are no shared term_taxonomy rows, there's nothing to do here.
     4070    $shared_tt_count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_taxonomy tt WHERE tt.term_id = %d AND tt.term_taxonomy_id != %d", $term_id, $term_taxonomy_id ) );
     4071    if ( ! $shared_tt_count ) {
     4072        return $term_id;
     4073    }
     4074
     4075    // Pull up data about the currently shared slug, which we'll use to populate the new one.
     4076    $shared_term = $wpdb->get_row( $wpdb->prepare( "SELECT t.* FROM $wpdb->terms t WHERE t.term_id = %d", $term_id ) );
     4077
     4078    $new_term_data = array(
     4079        'name' => $shared_term->name,
     4080        'slug' => $shared_term->slug,
     4081        'term_group' => $shared_term->term_group,
     4082    );
     4083
     4084    if ( false === $wpdb->insert( $wpdb->terms, $new_term_data ) ) {
     4085        return new WP_Error( 'db_insert_error', __( 'Could not split shared term.' ), $wpdb->last_error );
     4086    }
     4087
     4088    $new_term_id = (int) $wpdb->insert_id;
     4089
     4090    // Update the existing term_taxonomy to point to the newly created term.
     4091    $wpdb->update( $wpdb->term_taxonomy,
     4092        array( 'term_id' => $new_term_id ),
     4093        array( 'term_taxonomy_id' => $term_taxonomy_id )
     4094    );
     4095
     4096    // Clean the cache for term taxonomies formerly shared with the current term.
     4097    $shared_term_taxonomies = $wpdb->get_row( $wpdb->prepare( "SELECT taxonomy FROM $wpdb->term_taxonomy WHERE term_id = %d", $term_id ) );
     4098    foreach ( (array) $shared_term_taxonomies as $shared_term_taxonomy ) {
     4099        clean_term_cache( $term_id, $shared_term_taxonomy );
     4100    }
     4101
     4102    return $new_term_id;
    40414103}
    40424104
  • trunk/tests/phpunit/tests/term.php

    r30240 r30241  
    638638    }
    639639
     640    /**
     641     * @ticket 5809
     642     */
     643    public function test_wp_update_term_duplicate_slug_same_taxonomy() {
     644        register_taxonomy( 'wptests_tax', 'post' );
     645
     646        $t1 = $this->factory->term->create( array(
     647            'name' => 'Foo',
     648            'slug' => 'foo',
     649            'taxonomy' => 'wptests_tax',
     650        ) );
     651
     652        $t2 = $this->factory->term->create( array(
     653            'name' => 'Foo',
     654            'slug' => 'bar',
     655            'taxonomy' => 'wptests_tax',
     656        ) );
     657
     658        $updated = wp_update_term( $t2, 'wptests_tax', array(
     659            'slug' => 'foo',
     660        ) );
     661
     662        $this->assertWPError( $updated );
     663        $this->assertSame( 'duplicate_term_slug', $updated->get_error_code() );
     664    }
     665
     666    /**
     667     * @ticket 5809
     668     */
     669    public function test_wp_update_term_duplicate_slug_different_taxonomy() {
     670        register_taxonomy( 'wptests_tax', 'post' );
     671        register_taxonomy( 'wptests_tax_2', 'post' );
     672
     673        $t1 = $this->factory->term->create( array(
     674            'name' => 'Foo',
     675            'slug' => 'foo',
     676            'taxonomy' => 'wptests_tax',
     677        ) );
     678
     679        $t2 = $this->factory->term->create( array(
     680            'name' => 'Foo',
     681            'slug' => 'bar',
     682            'taxonomy' => 'wptests_tax_2',
     683        ) );
     684
     685        $updated = wp_update_term( $t2, 'wptests_tax_2', array(
     686            'slug' => 'foo',
     687        ) );
     688
     689        $this->assertWPError( $updated );
     690        $this->assertSame( 'duplicate_term_slug', $updated->get_error_code() );
     691    }
     692
     693    /**
     694     * @ticket 5809
     695     */
     696    public function test_wp_update_term_should_split_shared_term() {
     697        global $wpdb;
     698
     699        register_taxonomy( 'wptests_tax', 'post' );
     700        register_taxonomy( 'wptests_tax_2', 'post' );
     701
     702        $t1 = wp_insert_term( 'Foo', 'wptests_tax' );
     703        $t2 = wp_insert_term( 'Foo', 'wptests_tax_2' );
     704
     705        // Manually modify because split terms shouldn't naturally occur.
     706        $wpdb->update( $wpdb->term_taxonomy,
     707            array( 'term_id' => $t1['term_id'] ),
     708            array( 'term_taxonomy_id' => $t2['term_taxonomy_id'] ),
     709            array( '%d' ),
     710            array( '%d' )
     711        );
     712
     713        $posts = $this->factory->post->create_many( 2 );
     714        wp_set_object_terms( $posts[0], array( 'Foo' ), 'wptests_tax' );
     715        wp_set_object_terms( $posts[1], array( 'Foo' ), 'wptests_tax_2' );
     716
     717        // Verify that the terms are shared.
     718        $t1_terms = wp_get_object_terms( $posts[0], 'wptests_tax' );
     719        $t2_terms = wp_get_object_terms( $posts[1], 'wptests_tax_2' );
     720        $this->assertSame( $t1_terms[0]->term_id, $t2_terms[0]->term_id );
     721
     722        wp_update_term( $t2_terms[0]->term_id, 'wptests_tax_2', array(
     723            'name' => 'New Foo',
     724        ) );
     725
     726        $t1_terms = wp_get_object_terms( $posts[0], 'wptests_tax' );
     727        $t2_terms = wp_get_object_terms( $posts[1], 'wptests_tax_2' );
     728        $this->assertNotEquals( $t1_terms[0]->term_id, $t2_terms[0]->term_id );
     729    }
     730
     731    /**
     732     * @ticket 5809
     733     */
     734    public function test_wp_update_term_should_not_split_shared_term_before_410_schema_change() {
     735        global $wpdb;
     736
     737        $db_version = get_option( 'db_version' );
     738        update_option( 'db_version', 30055 );
     739
     740        register_taxonomy( 'wptests_tax', 'post' );
     741        register_taxonomy( 'wptests_tax_2', 'post' );
     742
     743        $t1 = wp_insert_term( 'Foo', 'wptests_tax' );
     744        $t2 = wp_insert_term( 'Foo', 'wptests_tax_2' );
     745
     746        // Manually modify because split terms shouldn't naturally occur.
     747        $wpdb->update( $wpdb->term_taxonomy,
     748            array( 'term_id' => $t1['term_id'] ),
     749            array( 'term_taxonomy_id' => $t2['term_taxonomy_id'] ),
     750            array( '%d' ),
     751            array( '%d' )
     752        );
     753
     754        $posts = $this->factory->post->create_many( 2 );
     755        wp_set_object_terms( $posts[0], array( 'Foo' ), 'wptests_tax' );
     756        wp_set_object_terms( $posts[1], array( 'Foo' ), 'wptests_tax_2' );
     757
     758        // Verify that the term is shared.
     759        $t1_terms = wp_get_object_terms( $posts[0], 'wptests_tax' );
     760        $t2_terms = wp_get_object_terms( $posts[1], 'wptests_tax_2' );
     761        $this->assertSame( $t1_terms[0]->term_id, $t2_terms[0]->term_id );
     762
     763        wp_update_term( $t2_terms[0]->term_id, 'wptests_tax_2', array(
     764            'name' => 'New Foo',
     765        ) );
     766
     767        // Term should still be shared.
     768        $t1_terms = wp_get_object_terms( $posts[0], 'wptests_tax' );
     769        $t2_terms = wp_get_object_terms( $posts[1], 'wptests_tax_2' );
     770        $this->assertSame( $t1_terms[0]->term_id, $t2_terms[0]->term_id );
     771
     772        update_option( 'db_version', $db_version );
     773    }
     774
    640775    public function test_wp_update_term_alias_of_no_term_group() {
    641776        register_taxonomy( 'wptests_tax', 'post' );
     
    14671602
    14681603    /**
    1469      * @ticket 5809
    1470      */
    1471     function test_update_shared_term() {
    1472         $random_tax = __FUNCTION__;
    1473 
    1474         register_taxonomy( $random_tax, 'post' );
    1475 
    1476         $post_id = $this->factory->post->create();
    1477 
    1478         $old_name = 'Initial';
    1479 
    1480         $t1 = wp_insert_term( $old_name, 'category' );
    1481         $t2 = wp_insert_term( $old_name, 'post_tag' );
    1482 
    1483         $this->assertEquals( $t1['term_id'], $t2['term_id'] );
    1484 
    1485         wp_set_post_categories( $post_id, array( $t1['term_id'] ) );
    1486         wp_set_post_tags( $post_id, array( (int) $t2['term_id'] ) );
    1487 
    1488         $new_name = 'Updated';
    1489 
    1490         // create the term in a third taxonomy, just to keep things interesting
    1491         $t3 = wp_insert_term( $old_name, $random_tax );
    1492         wp_set_post_terms( $post_id, array( (int) $t3['term_id'] ), $random_tax );
    1493         $this->assertPostHasTerms( $post_id, array( $t3['term_id'] ), $random_tax );
    1494 
    1495         $t2_updated = wp_update_term( $t2['term_id'], 'post_tag', array(
    1496             'name' => $new_name
    1497         ) );
    1498 
    1499         $this->assertNotEquals( $t2_updated['term_id'], $t3['term_id'] );
    1500 
    1501         // make sure the terms have split
    1502         $this->assertEquals( $old_name, get_term_field( 'name', $t1['term_id'], 'category' ) );
    1503         $this->assertEquals( $new_name, get_term_field( 'name', $t2_updated['term_id'], 'post_tag' ) );
    1504 
    1505         // and that they are still assigned to the correct post
    1506         $this->assertPostHasTerms( $post_id, array( $t1['term_id'] ), 'category' );
    1507         $this->assertPostHasTerms( $post_id, array( $t2_updated['term_id'] ), 'post_tag' );
    1508         $this->assertPostHasTerms( $post_id, array( $t3['term_id'] ), $random_tax );
    1509 
    1510         // clean up
    1511         unset( $GLOBALS['wp_taxonomies'][ $random_tax ] );
    1512     }
    1513 
    1514     /**
    15151604     * @ticket 25852
    15161605     */
Note: See TracChangeset for help on using the changeset viewer.