Make WordPress Core

Ticket #38280: 38280.9.diff

File 38280.9.diff, 14.2 KB (added by desrosj, 6 years ago)
  • src/wp-includes/taxonomy.php

     
    30913091        return true;
    30923092}
    30933093
     3094/**
     3095 * Retrieves the term count for a specific object type.
     3096 *
     3097 * @since 5.0.0
     3098 *
     3099 * @param int    $term_id     Term ID.
     3100 * @param string $taxonomy    Taxonomy name.
     3101 * @param string $object_type Object type.
     3102 *
     3103 * @return WP_Error|bool|int WP_Error if invalid taxonomy is passed,
     3104 *                           false if object is not in taxonomy,
     3105 *                           object term count otherwise.
     3106 */
     3107function wp_get_term_count_for_object_type( $term_id, $taxonomy, $object_type ) {
     3108        if ( ! taxonomy_exists( $taxonomy ) ) {
     3109                return new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy.' ) );
     3110        }
     3111
     3112        if ( ! is_object_in_taxonomy( $object_type, $taxonomy ) ) {
     3113                return false;
     3114        }
     3115
     3116        $term = get_term( $term_id, $taxonomy );
     3117
     3118        if ( is_wp_error( $term ) ) {
     3119                return $term;
     3120        }
     3121
     3122        if ( 0 === $term->count ) {
     3123                return 0;
     3124        }
     3125
     3126        $taxonomy_object = get_taxonomy( $taxonomy );
     3127
     3128        if ( 1 >= count( $taxonomy_object->object_type ) ) {
     3129                return $term->count;
     3130        }
     3131
     3132        $term_object_count = get_term_meta( $term_id, '_wp_object_count_' . $object_type, true );
     3133        if ( $term_object_count ) {
     3134                return (int) $term_object_count;
     3135        }
     3136
     3137        $counted_object_types = (array) get_term_meta( $term_id, '_wp_counted_object_types', true );
     3138
     3139        // When the object type is marked counted and no meta key exists, the count is 0.
     3140        if ( in_array( $object_type, $counted_object_types, true ) ) {
     3141                return 0;
     3142        }
     3143
     3144        // Term has not been counted for the object type.
     3145        if ( wp_update_term_count_now( array( $term_id ), $taxonomy ) ) {
     3146                return wp_get_term_count_for_object_type( $term_id, $taxonomy, $object_type );
     3147        }
     3148
     3149
     3150}
     3151
    30943152//
    30953153// Cache
    30963154//
     
    36053663 *
    36063664 * @access private
    36073665 * @since 2.3.0
     3666 * @since 5.0.0 Store term counts on a per object type basis in meta.
    36083667 *
    36093668 * @global wpdb $wpdb WordPress database abstraction object.
    36103669 *
     
    36343693        foreach ( (array) $terms as $term ) {
    36353694                $count = 0;
    36363695
     3696                // Remove previous counts to prevent stale data if an object type is removed from a taxonomy.
     3697                $counted_object_types = (array) get_term_meta( $term, '_wp_counted_object_types', true );
     3698
     3699                foreach ( $counted_object_types as $o_type ) {
     3700                        delete_term_meta( $term, '_wp_object_count_' . $o_type );
     3701                }
     3702
     3703                delete_term_meta( $term, '_wp_counted_object_types' );
     3704
     3705                $term_count_meta = array();
     3706
     3707                if ( $object_types ) {
     3708                        foreach ( $object_types as $type ) {
     3709                                $current_count = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_relationships, $wpdb->posts WHERE $wpdb->posts.ID = $wpdb->term_relationships.object_id AND post_status = 'publish' AND post_type = %s AND term_taxonomy_id = %d", $type, $term ) );
     3710
     3711                                $count += $current_count;
     3712                                $term_count_meta[ $type ] = $current_count;
     3713                        }
     3714                }
     3715
    36373716                // Attachments can be 'inherit' status, we need to base count off the parent's status if so.
    36383717                if ( $check_attachments ) {
    3639                         $count += (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_relationships, $wpdb->posts p1 WHERE p1.ID = $wpdb->term_relationships.object_id AND ( post_status = 'publish' OR ( post_status = 'inherit' AND post_parent > 0 AND ( SELECT post_status FROM $wpdb->posts WHERE ID = p1.post_parent ) = 'publish' ) ) AND post_type = 'attachment' AND term_taxonomy_id = %d", $term ) );
     3718                        $attachment_count = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_relationships, $wpdb->posts p1 WHERE p1.ID = $wpdb->term_relationships.object_id AND ( post_status = 'publish' OR ( post_status = 'inherit' AND post_parent > 0 AND ( SELECT post_status FROM $wpdb->posts WHERE ID = p1.post_parent ) = 'publish' ) ) AND post_type = 'attachment' AND term_taxonomy_id = %d", $term ) );
     3719
     3720                        $count += $attachment_count;
     3721                        $term_count_meta['attachment'] = $attachment_count;
     3722
     3723                        // Re-add attachment so the meta gets saved below.
     3724                        $object_types[] = 'attachment';
    36403725                }
    36413726
    3642                 if ( $object_types ) {
    3643                         $count += (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_relationships, $wpdb->posts WHERE $wpdb->posts.ID = $wpdb->term_relationships.object_id AND post_status = 'publish' AND post_type IN ('" . implode( "', '", $object_types ) . "') AND term_taxonomy_id = %d", $term ) );
     3727                // Save individual counts for each object type in term meta.
     3728                if ( 1 < count( $term_count_meta ) ) {
     3729                        foreach ( $object_types as $type ) {
     3730                                if ( ! empty( $term_count_meta[ $type ] ) ) {
     3731                                        update_term_meta( $term, '_wp_object_count_' . $type, (int) $term_count_meta[ $type ] );
     3732                                }
     3733                        }
     3734
     3735                        update_term_meta( $term, '_wp_counted_object_types', $object_types );
    36443736                }
    36453737
    36463738                /** This action is documented in wp-includes/taxonomy.php */
  • tests/phpunit/tests/term/wpGetTermCountForObjectType.php

     
     1<?php
     2
     3/**
     4 * Class Tests_Term_wpGetTermCountForObjectType
     5 *
     6 * @group taxonomy
     7 * @ticket 38280
     8 */
     9class Tests_Term_wpGetTermCountForObjectType extends WP_UnitTestCase {
     10
     11        /**
     12         * Test when a taxonomy is invalid.
     13         */
     14        public function test_wp_get_term_count_for_object_type_invalid_taxonomy() {
     15                $actual = wp_get_term_count_for_object_type( 0, 'does-not-exist', 'post' );
     16
     17                $this->assertWPError( $actual );
     18                $this->assertSame( 'invalid_taxonomy', $actual->get_error_code() );
     19        }
     20
     21        /**
     22         * Test when an object type is not in a taxonomy.
     23         */
     24        public function test_wp_get_term_count_for_object_type_object_not_in_taxonomy() {
     25                $this->assertFalse( wp_get_term_count_for_object_type( 0, 'category', 'page' ) );
     26        }
     27
     28        /**
     29         * Test when an invalid term is passed.
     30         */
     31        public function test_wp_get_term_count_for_object_type_invalid_term() {
     32                $actual = wp_get_term_count_for_object_type( 0, 'category', 'post' );
     33
     34                $this->assertWPError( $actual );
     35                $this->assertSame( 'invalid_term', $actual->get_error_code() );
     36        }
     37
     38        /**
     39         * Test when a taxonomy belongs to a single object type. No term meta should be
     40         * stored and the count property should be used.
     41         */
     42        public function test_wp_get_term_count_for_object_type_single_object_type() {
     43                $term_id = self::factory()->term->create( array( 'taxonomy' => 'category' ) );
     44                $post_id = self::factory()->post->create(
     45                        array(
     46                                'post_type'     => 'post',
     47                                'post_category' => array(
     48                                        $term_id,
     49                                ),
     50                        )
     51                );
     52
     53                $this->assertFalse( (bool) get_term_meta( $term_id, '_wp_counted_object_types', true ) );
     54                $this->assertEquals( 1, wp_get_term_count_for_object_type( $term_id, 'category', 'post' ) );
     55
     56                $term_object = get_term( $term_id, 'category' );
     57                $this->assertEquals( 1, $term_object->count );
     58
     59                wp_remove_object_terms( $post_id, array( $term_id ), 'category' );
     60
     61                $this->assertEquals( 0, wp_get_term_count_for_object_type( $term_id, 'category', 'post' ) );
     62
     63                $term_object = get_term( $term_id, 'category' );
     64                $this->assertEquals( 0, $term_object->count );
     65        }
     66
     67        /**
     68         * Test when a taxonomy belongs to multiple object types.
     69         */
     70        public function test_wp_get_term_count_for_object_type_multiple_object_types() {
     71                register_post_type( 'wptests_cpt' );
     72                register_taxonomy_for_object_type( 'category', 'wptests_cpt' );
     73
     74                $term_id        = self::factory()->term->create( array( 'taxonomy' => 'category' ) );
     75                $post_id        = self::factory()->post->create( array( 'post_type' => 'post' ) );
     76                $custom_post_id = self::factory()->post->create( array( 'post_type' => 'wptests_cpt' ) );
     77
     78                // When a term has no relationships, no meta should be stored.
     79                $this->assertEquals( 0, wp_get_term_count_for_object_type( $term_id, 'category', 'post' ) );
     80                $this->assertEquals( 0,  wp_get_term_count_for_object_type( $term_id, 'category', 'wptests_cpt' ) );
     81                $this->assertEmpty( get_term_meta( $term_id, '_wp_counted_object_types', true ) );
     82                $this->assertEmpty( get_term_meta( $term_id, '_wp_object_count_post', true ) );
     83                $this->assertEmpty( get_term_meta( $term_id, '_wp_object_count_wptests_cpt', true ) );
     84
     85                wp_set_object_terms( $post_id, array( $term_id ), 'category' );
     86
     87                // Term has relationships, meta should be stored caching types counted and counts for each type > 0.
     88                $this->assertEquals( 1, wp_get_term_count_for_object_type( $term_id, 'category', 'post' ) );
     89                $this->assertEquals( 0, wp_get_term_count_for_object_type( $term_id, 'category', 'wptests_cpt' ) );
     90                $this->assertEquals( array( 'post', 'wptests_cpt' ), get_term_meta( $term_id, '_wp_counted_object_types', true ) );
     91                $this->assertEquals( 1, get_term_meta( $term_id, '_wp_object_count_post', true ) );
     92                $this->assertEmpty( get_term_meta( $term_id, '_wp_object_count_wptests_cpt', true ) );
     93
     94                wp_set_object_terms( $custom_post_id, array( $term_id ), 'category' );
     95
     96                $this->assertEquals( 1, wp_get_term_count_for_object_type( $term_id, 'category', 'post' ) );
     97                $this->assertEquals( 1, wp_get_term_count_for_object_type( $term_id, 'category', 'wptests_cpt' ) );
     98                $this->assertEquals( array( 'post', 'wptests_cpt' ), get_term_meta( $term_id, '_wp_counted_object_types', true ) );
     99                $this->assertEquals( 1, get_term_meta( $term_id, '_wp_object_count_post', true ) );
     100                $this->assertEquals( 1, get_term_meta( $term_id, '_wp_object_count_wptests_cpt', true ) );
     101
     102                // Total count should be stored in the term's count property.
     103                $term_object = get_term( $term_id, 'category' );
     104                $this->assertEquals( 2, $term_object->count );
     105
     106                wp_remove_object_terms( $custom_post_id, array( $term_id ), 'category' );
     107
     108                // Object count cache should be removed.
     109                $this->assertEquals( 1, wp_get_term_count_for_object_type( $term_id, 'category', 'post' ) );
     110                $this->assertEquals( 0, wp_get_term_count_for_object_type( $term_id, 'category', 'wptests_cpt' ) );
     111                $this->assertEquals( array( 'post', 'wptests_cpt' ), get_term_meta( $term_id, '_wp_counted_object_types', true ) );
     112                $this->assertEquals( 1, get_term_meta( $term_id, '_wp_object_count_post', true ) );
     113                $this->assertEmpty( get_term_meta( $term_id, '_wp_object_count_wptests_cpt', true ) );
     114
     115                wp_remove_object_terms( $post_id, array( $term_id ), 'category' );
     116
     117                $this->assertEquals( 0, wp_get_term_count_for_object_type( $term_id, 'category', 'post' ) );
     118                $this->assertEquals( 0, wp_get_term_count_for_object_type( $term_id, 'category', 'wptests_cpt' ) );
     119                $this->assertEquals( array( 'post', 'wptests_cpt' ), get_term_meta( $term_id, '_wp_counted_object_types', true ) );
     120                $this->assertEmpty( get_term_meta( $term_id, '_wp_object_count_post', true ) );
     121                $this->assertEmpty( get_term_meta( $term_id, '_wp_object_count_wptests_cpt', true ) );
     122        }
     123
     124        /**
     125         * Test when a taxonomy belongs to multiple object types, one of which is attachments.
     126         */
     127        public function test_wp_get_term_count_for_object_type_multiple_object_types_attachment() {
     128                register_taxonomy_for_object_type( 'category', 'attachment' );
     129
     130                $term_id = self::factory()->term->create( array( 'taxonomy' => 'category' ) );
     131                $post_id = self::factory()->potst->create( array( 'post_type' => 'post' ) );
     132                $attachment_id = self::factory()->attachment->create_upload_object( DIR_TESTDATA . '/images/canola.jpg', $post_id );
     133
     134                // When a term has no relationships, no meta should be stored.
     135                $this->assertEquals( 0, wp_get_term_count_for_object_type( $term_id, 'category', 'post' ) );
     136                $this->assertEquals( 0, wp_get_term_count_for_object_type( $term_id, 'category', 'attachment' ) );
     137                $this->assertEmpty( get_term_meta( $term_id, '_wp_counted_object_types', true ) );
     138                $this->assertEmpty( get_term_meta( $term_id, '_wp_object_count_post', true ) );
     139                $this->assertEmpty( get_term_meta( $term_id, '_wp_object_count_attachment', true ) );
     140
     141                wp_set_object_terms( $post_id, array( $term_id ), 'category' );
     142
     143                // Term has relationships, meta should be stored caching types counted and counts for each type > 0.
     144                $this->assertEquals( 1, wp_get_term_count_for_object_type( $term_id, 'category', 'post' ) );
     145                $this->assertEquals( 0, wp_get_term_count_for_object_type( $term_id, 'category', 'attachment' ) );
     146                $this->assertEquals( array( 'post', 'attachment' ), get_term_meta( $term_id, '_wp_counted_object_types', true ) );
     147                $this->assertEquals( 1, get_term_meta( $term_id, '_wp_object_count_post', true ) );
     148                $this->assertEmpty( get_term_meta( $term_id, '_wp_object_count_attachment', true ) );
     149
     150                wp_set_object_terms( $attachment_id, array( $term_id ), 'category' );
     151
     152                $this->assertEquals( 1, wp_get_term_count_for_object_type( $term_id, 'category', 'post' ) );
     153                $this->assertEquals( 1, wp_get_term_count_for_object_type( $term_id, 'category', 'attachment' ) );
     154                $this->assertEquals( array( 'post', 'attachment' ), get_term_meta( $term_id, '_wp_counted_object_types', true ) );
     155                $this->assertEquals( 1, get_term_meta( $term_id, '_wp_object_count_post', true ) );
     156                $this->assertEquals( 1, get_term_meta( $term_id, '_wp_object_count_attachment', true ) );
     157
     158                // Total count should be stored in the term's count property.
     159                $term_object = get_term( $term_id, 'category' );
     160                $this->assertEquals( 2, $term_object->count );
     161
     162                wp_remove_object_terms( $post_id, array( $term_id ), 'category' );
     163
     164                // Object count cache should be removed.
     165                $this->assertEquals( 0, wp_get_term_count_for_object_type( $term_id, 'category', 'post' ) );
     166                $this->assertEquals( 1, wp_get_term_count_for_object_type( $term_id, 'category', 'attachment' ) );
     167                $this->assertEmpty( get_term_meta( $term_id, '_wp_object_count_post', true ) );
     168                $this->assertEquals( 1, get_term_meta( $term_id, '_wp_object_count_attachment', true ) );
     169                wp_remove_object_terms( $attachment_id, array( $term_id ), 'category' );
     170
     171                $this->assertEquals( 0, wp_get_term_count_for_object_type( $term_id, 'category', 'post' ) );
     172                $this->assertEquals( 0, wp_get_term_count_for_object_type( $term_id, 'category', 'attachment' ) );
     173                $this->assertEquals( array( 'post', 'attachment' ), get_term_meta( $term_id, '_wp_counted_object_types', true ) );
     174                $this->assertEmpty( get_term_meta( $term_id, '_wp_object_count_post', true ) );
     175                $this->assertEmpty( get_term_meta( $term_id, '_wp_object_count_attachment', true ) );
     176        }
     177}