Ticket #24386: 24386.diff
File 24386.diff, 13.1 KB (added by , 4 years ago) |
---|
-
src/wp-includes/class-walker-pad-term-counts.php
1 <?php 2 /** 3 * Taxonomy API: Walker_Pad_Term_Counts class 4 * 5 * @package WordPress 6 * @subpackage Template 7 * @since 5.7.0 8 */ 9 10 /** 11 * Core class used to pad term counts. 12 * 13 * @since 5.7.0 14 * 15 * @see Walker 16 */ 17 class Walker_Pad_Term_Counts { 18 19 /** 20 * Holds all the ancestors of the current term. 21 * 22 * @since 5.7.0 23 * @var array 24 */ 25 public $ancestors = array(); 26 27 /** 28 * Adds new term to the ancestors list. 29 * 30 * @since 5.7.0 31 * 32 * @param object $term The data object. 33 */ 34 public function start_lvl( &$term ) { 35 array_push( $this->ancestors, $term ); 36 } 37 38 /** 39 * Removes the last term from the ancestors list. 40 * 41 * @since 5.7.0 42 * 43 * @param object $term The data object. 44 */ 45 public function end_lvl( $term ) { 46 array_pop( $this->ancestors ); 47 } 48 49 /** 50 * Add terms count to all it's ancestors. 51 * 52 * @since 5.7.0 53 * 54 * @param object $term The data object. 55 */ 56 public function count_term( $term ) { 57 foreach ( $this->ancestors as &$ancestor ) { 58 $ancestor->count += $term->count; 59 } 60 } 61 62 /** 63 * Traverse terms to add descendants counts to their ancestors. 64 * 65 * This method should not be called directly, use the walk() method instead. 66 * 67 * @since 5.7.0 68 * 69 * @param object $term Data object. 70 * @param array $children_terms List of terms to continue traversing (passed by reference). 71 */ 72 public function process_term( $term, &$children_terms ) { 73 if ( ! $term ) { 74 return; 75 } 76 77 $id = $term->term_id; 78 79 $this->count_term( $term ); 80 81 // Descend only when there are children for this term. 82 if ( isset( $children_terms[ $id ] ) ) { 83 84 foreach ( $children_terms[ $id ] as $child ) { 85 86 if ( ! isset( $newlevel ) ) { 87 $newlevel = true; 88 // Start the new level. 89 $this->start_lvl( $term ); 90 } 91 $this->process_term( $child, $children_terms ); 92 } 93 unset( $children_terms[ $id ] ); 94 } 95 96 if ( isset( $newlevel ) && $newlevel ) { 97 // End the child level. 98 $this->end_lvl( $term ); 99 } 100 } 101 102 /** 103 * Process the array of terms hierarchically. 104 * 105 * Does not assume any existing order of terms. 106 * 107 * @since 5.7.0 108 * 109 * @param array $terms An array of terms. 110 */ 111 public function walk( $terms, $debug = false ) { 112 // Invalid parameter or nothing to walk. 113 if ( empty( $terms ) ) { 114 return; 115 } 116 117 /* 118 * Separate terms into two buckets: top level and children terms. 119 * Children_terms is two dimensional array, eg. 120 * Children_terms[10][] contains all sub-terms whose parent is 10. 121 */ 122 $top_level_terms = array(); 123 $children_terms = array(); 124 foreach ( $terms as $t ) { 125 if ( empty( $t->parent ) ) { 126 $top_level_terms[] = $t; 127 } else { 128 $children_terms[ $t->parent ][] = $t; 129 } 130 } 131 132 /** 133 * In case there are no children, there is nothing to do here. 134 */ 135 if ( empty( $children_terms ) ) { 136 return; 137 } 138 139 /* 140 * When none of the terms is top level. 141 * Assume the terms with no parent in among terms passed to the method are top level terms. 142 */ 143 if ( empty( $top_level_terms ) ) { 144 $all_term_ids = wp_list_pluck( $terms, 'term_id' ); 145 foreach ( $children_terms as $parent => $ts ) { 146 foreach ( $ts as $key => $t ) { 147 if ( ! in_array( $parent, $all_term_ids, true ) ) { 148 $top_level_terms[] = $t; 149 unset( $children_terms[ $parent ][ $key ] ); 150 } 151 } 152 } 153 } 154 155 // Process the terms. 156 foreach ( $top_level_terms as $t ) { 157 $this->process_term( $t, $children_terms ); 158 } 159 } 160 } -
src/wp-includes/taxonomy.php
Property changes on: src/wp-includes/class-walker-pad-term-counts.php ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property
3738 3738 * @access private 3739 3739 * @since 2.3.0 3740 3740 * 3741 * @global wpdb $wpdb WordPress database abstraction object.3742 *3743 3741 * @param array $terms List of term objects (passed by reference). 3744 3742 * @param string $taxonomy Term context. 3745 3743 */ 3746 3744 function _pad_term_counts( &$terms, $taxonomy ) { 3747 global $wpdb;3748 3749 3745 // This function only works for hierarchical taxonomies like post categories. 3750 3746 if ( ! is_taxonomy_hierarchical( $taxonomy ) ) { 3751 3747 return; … … 3757 3753 return; 3758 3754 } 3759 3755 3760 $term_items = array(); 3761 $terms_by_id = array(); 3762 $term_ids = array(); 3763 3764 foreach ( (array) $terms as $key => $term ) { 3765 $terms_by_id[ $term->term_id ] = & $terms[ $key ]; 3766 $term_ids[ $term->term_taxonomy_id ] = $term->term_id; 3767 } 3768 3769 // Get the object and term IDs and stick them in a lookup table. 3770 $tax_obj = get_taxonomy( $taxonomy ); 3771 $object_types = esc_sql( $tax_obj->object_type ); 3772 $results = $wpdb->get_results( "SELECT object_id, term_taxonomy_id FROM $wpdb->term_relationships INNER JOIN $wpdb->posts ON object_id = ID WHERE term_taxonomy_id IN (" . implode( ',', array_keys( $term_ids ) ) . ") AND post_type IN ('" . implode( "', '", $object_types ) . "') AND post_status = 'publish'" ); 3773 3774 foreach ( $results as $row ) { 3775 $id = $term_ids[ $row->term_taxonomy_id ]; 3776 3777 $term_items[ $id ][ $row->object_id ] = isset( $term_items[ $id ][ $row->object_id ] ) ? ++$term_items[ $id ][ $row->object_id ] : 1; 3778 } 3779 3780 // Touch every ancestor's lookup row for each post in each term. 3781 foreach ( $term_ids as $term_id ) { 3782 $child = $term_id; 3783 $ancestors = array(); 3784 while ( ! empty( $terms_by_id[ $child ] ) && $parent = $terms_by_id[ $child ]->parent ) { 3785 $ancestors[] = $child; 3786 3787 if ( ! empty( $term_items[ $term_id ] ) ) { 3788 foreach ( $term_items[ $term_id ] as $item_id => $touches ) { 3789 $term_items[ $parent ][ $item_id ] = isset( $term_items[ $parent ][ $item_id ] ) ? ++$term_items[ $parent ][ $item_id ] : 1; 3790 } 3791 } 3792 3793 $child = $parent; 3794 3795 if ( in_array( $parent, $ancestors, true ) ) { 3796 break; 3797 } 3798 } 3799 } 3800 3801 // Transfer the touched cells. 3802 foreach ( (array) $term_items as $id => $items ) { 3803 if ( isset( $terms_by_id[ $id ] ) ) { 3804 $terms_by_id[ $id ]->count = count( $items ); 3805 } 3806 } 3756 $walker = new Walker_Pad_Term_Counts(); 3757 $walker->walk( $terms ); 3807 3758 } 3808 3759 3809 3760 /** -
src/wp-settings.php
213 213 require ABSPATH . WPINC . '/class-wp-term.php'; 214 214 require ABSPATH . WPINC . '/class-wp-term-query.php'; 215 215 require ABSPATH . WPINC . '/class-wp-tax-query.php'; 216 require ABSPATH . WPINC . '/class-walker-pad-term-counts.php'; 216 217 require ABSPATH . WPINC . '/update.php'; 217 218 require ABSPATH . WPINC . '/canonical.php'; 218 219 require ABSPATH . WPINC . '/shortcodes.php'; -
tests/phpunit/tests/taxonomy/padTermCounts.php
1 <?php 2 3 /** 4 * @group taxonomy 5 */ 6 class Tests_Taxonomy_Pad_Term_Counts extends WP_UnitTestCase { 7 8 public function test_no_child_terms() { 9 $terms = self::factory()->term->create_many( 2, array( 'taxonomy' => 'category' ) ); 10 11 $terms = array_map( 'get_term', $terms ); 12 _pad_term_counts( $terms, 'category' ); 13 14 foreach( $terms as $term ) { 15 $this->assertEquals( 0, $term->count ); 16 } 17 } 18 19 public function test_single_post_in_a_category() { 20 $terms = array(); 21 $terms['parent'] = self::create_term_with_that_many_posts( 1, array( 'taxonomy' => 'category' ) ); 22 23 $terms = array_map( 'get_term', $terms ); 24 _pad_term_counts( $terms, 'category' ); 25 26 $this->assertEquals( 1, $terms['parent']->count ); 27 } 28 29 public function test_single_child() { 30 $terms = array(); 31 $terms['parent'] = self::factory()->term->create( array( 'taxonomy' => 'category' ) ); 32 $terms['child'] = self::create_term_with_that_many_posts( 1, array( 'taxonomy' => 'category', 'parent' => $terms['parent'] ) ); 33 34 $terms = array_map( 'get_term', $terms ); 35 _pad_term_counts( $terms, 'category' ); 36 37 foreach( $terms as $term ) { 38 $this->assertEquals( 1, $term->count ); 39 } 40 } 41 42 public function test_single_grandchild() { 43 $terms = array(); 44 $terms['parent'] = self::factory()->term->create( array( 'taxonomy' => 'category' ) ); 45 $terms['child'] = self::factory()->term->create( array( 'taxonomy' => 'category', 'parent' => $terms['parent'] ) ); 46 $terms['grandchild'] = self::create_term_with_that_many_posts( 1, array( 'taxonomy' => 'category', 'parent' => $terms['child'] ) ); 47 48 $terms = array_map( 'get_term', $terms ); 49 _pad_term_counts( $terms, 'category' ); 50 51 foreach( $terms as $term ) { 52 $this->assertEquals( 1, $term->count ); 53 } 54 } 55 56 public function test_both_parent_and_grandchild() { 57 $terms = array(); 58 $terms['parent'] = self::create_term_with_that_many_posts( 1, array( 'taxonomy' => 'category', 'parent' => 0 ) ); 59 $terms['child'] = self::factory()->term->create( array( 'taxonomy' => 'category', 'parent' => $terms['parent'] ) ); 60 $terms['grandchild'] = self::create_term_with_that_many_posts( 1, array( 'taxonomy' => 'category', 'parent' => $terms['child'] ) ); 61 62 $terms = array_map( 'get_term', $terms ); 63 _pad_term_counts( $terms, 'category' ); 64 65 $this->assertEquals( 2, $terms['parent']->count ); 66 $this->assertEquals( 1, $terms['child']->count ); 67 $this->assertEquals( 1, $terms['grandchild']->count ); 68 } 69 70 public function test_many_children_and_grandchildren() { 71 $terms = array(); 72 $terms['parent'] = self::create_term_with_that_many_posts( 1, array( 'taxonomy' => 'category', 'parent' => 0 ) ); 73 74 $terms['child_0_posts'] = self::factory()->term->create( array( 'taxonomy' => 'category', 'parent' => $terms['parent'] ) ); 75 $terms['grandchild_3_posts'] = self::create_term_with_that_many_posts( 3, array( 'taxonomy' => 'category', 'parent' => $terms['child_0_posts'] ) ); 76 77 $terms['child_2_posts'] = self::create_term_with_that_many_posts( 2, array( 'taxonomy' => 'category', 'parent' => $terms['parent'] ) ); 78 $terms['grandchild_2_posts'] = self::create_term_with_that_many_posts( 2, array( 'taxonomy' => 'category', 'parent' => $terms['child_2_posts'] ) ); 79 $terms['grandchild_1_posts'] = self::create_term_with_that_many_posts( 1, array( 'taxonomy' => 'category', 'parent' => $terms['child_2_posts'] ) ); 80 81 $terms['grandgrandchild_1_posts'] = self::create_term_with_that_many_posts( 1, array( 'taxonomy' => 'category', 'parent' => $terms['grandchild_1_posts'] ) ); 82 83 $terms = array_map( 'get_term', $terms ); 84 _pad_term_counts( $terms, 'category' ); 85 86 $this->assertEquals( 10, $terms['parent']->count ); 87 $this->assertEquals( 3, $terms['child_0_posts']->count ); 88 $this->assertEquals( 3, $terms['grandchild_3_posts']->count ); 89 $this->assertEquals( 6, $terms['child_2_posts']->count ); 90 $this->assertEquals( 2, $terms['grandchild_2_posts']->count ); 91 $this->assertEquals( 2, $terms['grandchild_1_posts']->count ); 92 $this->assertEquals( 1, $terms['grandgrandchild_1_posts']->count ); 93 } 94 95 public function test_no_top_level_terms() { 96 $terms = array(); 97 $parent = self::factory()->term->create( array( 'taxonomy' => 'category', 'parent' => 0 ) ); 98 $terms['child_0_posts'] = self::factory()->term->create( array( 'taxonomy' => 'category', 'parent' => $parent ) ); 99 $terms['grandchild_3_posts'] = self::create_term_with_that_many_posts( 3, array( 'taxonomy' => 'category', 'parent' => $terms['child_0_posts'] ) ); 100 101 $terms['child_2_posts'] = self::create_term_with_that_many_posts( 2, array( 'taxonomy' => 'category', 'parent' => $parent ) ); 102 $terms['grandchild_2_posts'] = self::create_term_with_that_many_posts( 2, array( 'taxonomy' => 'category', 'parent' => $terms['child_2_posts'] ) ); 103 104 $terms = array_map( 'get_term', $terms ); 105 _pad_term_counts( $terms, 'category' ); 106 107 $parent = get_term( $parent ); 108 $this->assertEquals( 0, $parent->count ); 109 $this->assertEquals( 3, $terms['child_0_posts']->count ); 110 $this->assertEquals( 3, $terms['grandchild_3_posts']->count ); 111 $this->assertEquals( 4, $terms['child_2_posts']->count ); 112 $this->assertEquals( 2, $terms['grandchild_2_posts']->count ); 113 } 114 115 private static function create_term_with_that_many_posts( $posts_no, $args ) { 116 $term = self::factory()->term->create( $args ); 117 if ( 0 < $posts_no ) { 118 $posts = self::factory()->post->create_many( $posts_no, array( 'post_category' => (array) $term ) ); 119 } 120 return $term; 121 } 122 }