WordPress.org

Make WordPress Core

Ticket #34848: 34848.4.diff

File 34848.4.diff, 12.4 KB (added by patrickgarman, 3 years ago)

Update SQL Queries && Add Unit Tests

  • src/wp-includes/meta.php

    diff --git src/wp-includes/meta.php src/wp-includes/meta.php
    index 010822fdc8..fbed19d189 100644
    function add_metadata( $meta_type, $object_id, $meta_key, $meta_value, $unique = 
    132132        return $mid;
    133133}
    134134
     135/**
     136 * Add multiple metadatas for the specified object. Similar to calling add_metadata for each metadata individually,
     137 * and is only applicable for unique meta data. If a meta key already exists for an object it will not be stored.
     138 *
     139 * @since x.x.x
     140 *
     141 * @global wpdb $wpdb WordPress database abstraction object.
     142 *
     143 * @param string $meta_type  Type of object metadata is for (e.g., comment, post, or user)
     144 * @param int    $object_id  ID of the object metadata is for
     145 * @param array  $meta_data  Metadata as a key/value pair array. Values must be serializable if non-scalar.
     146 * @return int|false The meta ID on success, false on failure.
     147 */
     148function add_metadatas( $meta_type, $object_id, $meta_data ) {
     149        global $wpdb;
     150
     151        if ( ! $meta_type || ! is_numeric( $object_id ) || ! is_array( $meta_data ) ) {
     152                return false;
     153        }
     154
     155        $object_id = absint( $object_id );
     156        if ( ! $object_id ) {
     157                return false;
     158        }
     159
     160        $table = _get_meta_table( $meta_type );
     161        if ( ! $table ) {
     162                return false;
     163        }
     164
     165        $column = sanitize_key( $meta_type . '_id' );
     166
     167        /**
     168         * Filters whether to add metadata of a specific type.
     169         *
     170         * The dynamic portion of the hook, `$meta_type`, refers to the meta
     171         * object type (comment, post, or user). Returning a non-null value
     172         * will effectively short-circuit the function.
     173         *
     174         * @since 3.1.0
     175         *
     176         * @param null|bool $check      Whether to allow adding metadata for the given type.
     177         * @param int       $object_id  Object ID.
     178         * @param array     $meta_data  Meta data, an array of meta keys and meta datas, meta values must be serializable
     179         *                              if non-scalar.
     180         */
     181        $check = apply_filters( "add_{$meta_type}_metadatas", null, $object_id, $meta_data );
     182        if ( null !== $check ) {
     183                return $check;
     184        }
     185
     186        $_meta_data = array();
     187        foreach( $meta_data as $key => $value ) {
     188                if ( 0 == absint( $wpdb->get_var(
     189                                $wpdb->prepare( "SELECT COUNT(*) FROM $table WHERE meta_key = %s AND $column = %d", $key, $object_id )
     190                        ) ) ) {
     191                        $key = wp_unslash( $key );
     192                        $value = wp_unslash( sanitize_meta( $key, $value, $meta_type ) );
     193
     194                        $_meta_data[ $key ] = maybe_serialize( $value );
     195
     196                        /**
     197                         * Fires immediately before meta of a specific type is added.
     198                         *
     199                         * The dynamic portion of the hook, `$meta_type`, refers to the meta
     200                         * object type (comment, post, or user).
     201                         *
     202                         * @since 3.1.0
     203                         *
     204                         * @param int    $object_id  Object ID.
     205                         * @param string $meta_key   Meta key.
     206                         * @param mixed  $meta_value Meta value.
     207                         */
     208                        do_action( "add_{$meta_type}_meta", $object_id, $key, $value );
     209
     210                }
     211        }
     212
     213        $rows = array();
     214        if( ! empty( $_meta_data ) ) {
     215                $sql = "INSERT INTO {$table} ({$column}, meta_key, meta_value) VALUES ";
     216
     217                $comma = false;
     218                foreach( $_meta_data as $key => $value ) {
     219                        if( true == $comma ) {
     220                                $sql .= ',';
     221                        }
     222
     223                        $sql = $wpdb->prepare( $sql . " (%d, %s, %s)", $object_id, $key, $value );
     224                        $comma = true;
     225                }
     226        }
     227
     228        $result = $wpdb->query( $sql );
     229
     230        if ( ! $result ) {
     231                return false;
     232        }
     233
     234        wp_cache_delete($object_id, $meta_type . '_meta');
     235
     236        return true;
     237}
     238
    135239/**
    136240 * Update metadata for the specified object. If no value already exists for the specified object
    137241 * ID and metadata key, the metadata will be added.
    function delete_metadata( $meta_type, $object_id, $meta_key, $meta_value = '', $ 
    462566        return true;
    463567}
    464568
     569/**
     570 * Delete metadatas for the specified object.
     571 *
     572 * @since x.x.x
     573 *
     574 * @global wpdb $wpdb WordPress database abstraction object.
     575 *
     576 * @param string $meta_type  Type of object metadata is for (e.g., comment, post, or user)
     577 * @param int    $object_id  ID of the object metadata is for
     578 * @param array  $meta_keys  Metadata keys to be deleted.
     579 * @return bool  True on successful delete, false on failure.
     580 */
     581function delete_metadatas( $meta_type, $object_id, $meta_keys ) {
     582        global $wpdb;
     583
     584        if ( ! $meta_type || ! is_numeric( $object_id ) && ! is_array( $meta_keys ) ) {
     585                return false;
     586        }
     587
     588        $object_id = absint( $object_id );
     589        if ( ! $object_id ) {
     590                return false;
     591        }
     592
     593        $table = _get_meta_table( $meta_type );
     594        if ( ! $table ) {
     595                return false;
     596        }
     597
     598        $type_column = sanitize_key( $meta_type . '_id' );
     599        $id_column   = 'user' == $meta_type ? 'umeta_id' : 'meta_id';
     600        // expected_slashed ($meta_key)
     601        $meta_key = array_map( 'wp_unslash', $meta_keys );
     602
     603        /**
     604         * Filters whether to delete metadata of a specific type.
     605         *
     606         * The dynamic portion of the hook, `$meta_type`, refers to the meta
     607         * object type (comment, post, or user). Returning a non-null value
     608         * will effectively short-circuit the function.
     609         *
     610         * @since x.x.x
     611         *
     612         * @param null|bool $delete     Whether to allow metadata deletion of the given type.
     613         * @param int       $object_id  Object ID.
     614         * @param array     $meta_keys  Meta keys.
     615         */
     616        $check = apply_filters( "delete_{$meta_type}_metadatas", null, $object_id, $meta_keys );
     617        if ( null !== $check ) {
     618                return (bool) $check;
     619        }
     620
     621        /**
     622         * Fires immediately before deleting metadata of a specific type.
     623         *
     624         * The dynamic portion of the hook, `$meta_type`, refers to the meta
     625         * object type (comment, post, or user).
     626         *
     627         * @since x.x.x
     628         *
     629         * @param int    $object_id  Object ID.
     630         * @param string $meta_keys  Meta keys.
     631         */
     632        do_action( "delete_{$meta_type}_metas", $object_id, $meta_keys );
     633        $sql = $wpdb->prepare( "DELETE FROM {$table} WHERE {$type_column} = %d and meta_key IN( ", $object_id );
     634        $comma = false;
     635        foreach( $meta_keys as $meta_key ) {
     636                if( true == $comma ) {
     637                        $sql .= ', ';
     638                }
     639                $sql = $wpdb->prepare( $sql . " %s", $meta_key );
     640                $comma = true;
     641        }
     642        $sql .= " )";
     643        $count = $wpdb->query( $sql );
     644
     645        if ( ! $count ) {
     646                return false;
     647        }
     648        wp_cache_delete($object_id, $meta_type . '_meta');
     649
     650        /**
     651         * Fires immediately after deleting metadata of a specific type.
     652         *
     653         * The dynamic portion of the hook name, `$meta_type`, refers to the meta
     654         * object type (comment, post, or user).
     655         *
     656         * @since x.x.x
     657         *
     658         * @param int    $object_id  Object ID.
     659         * @param string $meta_keys  Meta key.
     660         */
     661        do_action( "deleted_{$meta_type}_metas", $object_id, $meta_keys );
     662
     663        return true;
     664}
     665
    465666/**
    466667 * Retrieve metadata for the specified object.
    467668 *
  • src/wp-includes/post.php

    diff --git src/wp-includes/post.php src/wp-includes/post.php
    index 45fb3573b8..77419a7f3d 100644
    function add_post_meta( $post_id, $meta_key, $meta_value, $unique = false ) { 
    18231823        return $added;
    18241824}
    18251825
     1826/**
     1827 * Add meta datas to a post.
     1828 *
     1829 * Post meta data is called "Custom Fields" on the Administration Screen.
     1830 *
     1831 * @since x.x.x
     1832 *
     1833 * @param int    $post_id    Post ID.
     1834 * @param array  $meta_data  Metadata as a key/value pair array. Values must be serializable if non-scalar.
     1835 * @return bool  Was the data inserted
     1836 */
     1837function add_post_metas( $post_id, $meta_data ) {
     1838        // Make sure meta is added to the post, not a revision.
     1839        if ( $the_post = wp_is_post_revision( $post_id ) ) {
     1840                $post_id = $the_post;
     1841        }
     1842
     1843        $added =  add_metadatas( 'post', $post_id, $meta_data );
     1844        if ( $added ) {
     1845                wp_cache_set( 'last_changed', microtime(), 'posts' );
     1846        }
     1847        return $added;
     1848}
     1849
    18261850/**
    18271851 * Remove metadata matching criteria from a post.
    18281852 *
    function delete_post_meta( $post_id, $meta_key, $meta_value = '' ) { 
    18511875        return $deleted;
    18521876}
    18531877
     1878/**
     1879 * Remove metadatas matching criteria from a post.
     1880 *
     1881 * @since x.x.x
     1882 *
     1883 * @param int    $post_id    Post ID.
     1884 * @param array $meta_keys  Meta keys that should be deleted.
     1885 * @return bool True on success, false on failure.
     1886 */
     1887function delete_post_metas( $post_id, $meta_keys ) {
     1888        // Make sure meta is added to the post, not a revision.
     1889        if ( $the_post = wp_is_post_revision( $post_id ) ) {
     1890                $post_id = $the_post;
     1891        }
     1892
     1893        $deleted = delete_metadatas( 'post', $post_id, $meta_keys );
     1894        if ( $deleted ) {
     1895                wp_cache_set( 'last_changed', microtime(), 'posts' );
     1896        }
     1897        return $deleted;
     1898}
     1899
    18541900/**
    18551901 * Retrieve post meta field for a post.
    18561902 *
    function update_post_meta( $post_id, $meta_key, $meta_value, $prev_value = '' ) 
    19001946        return $updated;
    19011947}
    19021948
     1949/**
     1950 * Update metadatas on a post. This is done by deleting all the post meta and then re-inserting it.
     1951 *
     1952 * To update post meta another way it would require two calls of update_post_meta
     1953 * Each update_post_meta call creates 1 select and 1 update mysql query
     1954 * 2 update_post_meta calls creates 2 select and 2 update queries
     1955 * update_post_metas creates 1 delete query, 1 insert query, and 1 select for every post meta being added
     1956 *
     1957 * Updating X metas:
     1958 * update_post_meta  = X selects, X updates
     1959 * update_post_metas = X selects, 1 delete, 1 insert
     1960 *
     1961 * The benefits of update_post_metas grows the more post metas being updated.
     1962 *
     1963 * Example: Updating 22 address metas on a post
     1964 * update_post_meta  = 22 selects, 22 updates
     1965 * update_post_metas = 22 selects, 1 delete, 1 insert
     1966 *
     1967 * @since x.x.x
     1968 *
     1969 * @param int    $post_id    Post ID.
     1970 * @param array  $meta_data  Metadata as a key/value pair array. Values must be serializable if non-scalar.
     1971 * @return int|bool Meta ID if the key didn't exist, true on successful update, false on failure.
     1972 */
     1973function update_post_metas( $post_id, $meta_data ) {
     1974        // Make sure meta is added to the post, not a revision.
     1975        if ( $the_post = wp_is_post_revision( $post_id ) ) {
     1976                $post_id = $the_post;
     1977        }
     1978
     1979        delete_post_metas( $post_id, array_keys( $meta_data ) );
     1980
     1981        $updated = add_post_metas( $post_id, $meta_data );
     1982        if ( $updated ) {
     1983                wp_cache_set( 'last_changed', microtime(), 'posts' );
     1984        }
     1985        return $updated;
     1986}
     1987
    19031988/**
    19041989 * Delete everything from post meta matching meta key.
    19051990 *
  • new file tests/phpunit/tests/meta/BulkMetadata.php

    diff --git tests/phpunit/tests/meta/BulkMetadata.php tests/phpunit/tests/meta/BulkMetadata.php
    new file mode 100644
    index 0000000000..f39ded42df
    - +  
     1<?php
     2
     3/**
     4 * @group meta
     5 * @covers ::update_metadata
     6 */
     7class Tests_Meta_BulkMetadata extends WP_UnitTestCase {
     8
     9        /**
     10         * @ticket 34848
     11         */
     12        public function test_adding_bulk_metadata() {
     13                add_post_metas( 123, array(
     14                        'foo'   => 'bar',
     15                        'bar'   => 'foo',
     16                        'array' => array(
     17                                'foobar' => 'barfoo'
     18                        )
     19                ) );
     20
     21                $foo = get_post_meta( 123, 'foo', true );
     22                $this->assertSame( $foo, 'bar' );
     23
     24                $bar = get_post_meta( 123, 'bar', true );
     25                $this->assertSame( $bar, 'foo' );
     26
     27                $array = get_post_meta( 123, 'array', true );
     28                $this->assertSame( $array, array(
     29                        'foobar' => 'barfoo'
     30                ) );
     31        }
     32
     33        /**
     34         * @ticket 34848
     35         */
     36        public function test_updating_bulk_metadata() {
     37                add_post_metas( 123, array(
     38                        'foo'   => 'bar',
     39                        'bar'   => 'foo',
     40                        'array' => array(
     41                                'foobar' => 'barfoo'
     42                        )
     43                ) );
     44
     45                $foo = get_post_meta( 123, 'foo', true );
     46                $this->assertSame( $foo, 'bar' );
     47
     48                $bar = get_post_meta( 123, 'bar', true );
     49                $this->assertSame( $bar, 'foo' );
     50
     51                $array = get_post_meta( 123, 'array', true );
     52                $this->assertSame( $array, array(
     53                        'foobar' => 'barfoo'
     54                ) );
     55
     56                update_post_metas( 123, array(
     57                        'foo'   => 'foo',
     58                        'bar'   => 'bar',
     59                        'array' => array(
     60                                'foobar' => 'foobar'
     61                        )
     62                ) );
     63
     64                $foo = get_post_meta( 123, 'foo', true );
     65                $this->assertSame( $foo, 'foo' );
     66
     67                $bar = get_post_meta( 123, 'bar', true );
     68                $this->assertSame( $bar, 'bar' );
     69
     70                $array = get_post_meta( 123, 'array', true );
     71                $this->assertSame( $array, array(
     72                        'foobar' => 'foobar'
     73                ) );
     74        }
     75
     76        /**
     77         * @ticket 34848
     78         */
     79        public function test_deleting_bulk_metadata() {
     80                add_post_metas( 123, array(
     81                        'foo'   => 'bar',
     82                        'bar'   => 'foo',
     83                        'array' => array(
     84                                'foobar' => 'barfoo'
     85                        )
     86                ) );
     87
     88                $foo = get_post_meta( 123, 'foo', true );
     89                $this->assertSame( $foo, 'bar' );
     90
     91                $bar = get_post_meta( 123, 'bar', true );
     92                $this->assertSame( $bar, 'foo' );
     93
     94                $array = get_post_meta( 123, 'array', true );
     95                $this->assertSame( $array, array(
     96                        'foobar' => 'barfoo'
     97                ) );
     98
     99                delete_post_metas( 123, array(
     100                        'foo',
     101                        'bar',
     102                    'array'
     103                ) );
     104
     105                $foo = get_post_meta( 123, 'foo', true );
     106                $this->assertSame( $foo, '' );
     107
     108                $bar = get_post_meta( 123, 'bar', true );
     109                $this->assertSame( $bar, '' );
     110
     111                $array = get_post_meta( 123, 'array', true );
     112                $this->assertSame( $array, '' );
     113        }
     114
     115}