6 comments

  1. This function worked for me:

    function get_complete_meta( $post_id, $meta_key ) {
      global $wpdb;
      $mid = $wpdb->get_results( $wpdb->prepare("SELECT * FROM $wpdb->postmeta WHERE post_id = %d AND meta_key = %s", $post_id, $meta_key) );
      if( $mid != '' )
        return $mid;
    
      return false;
    }
    

    it will return an array of objects like:

    Array
    (
        [0] => stdClass Object
            (
                [meta_id] => 1002
                [post_id] => 1
                [meta_key] => my_key
                [meta_value] => my_value
            )
        [1] => stdClass Object
            (
                [meta_id] => 1003
                [post_id] => 668
                [meta_key] => my_key
                [meta_value] => another value
            )
    )
    
  2. I don’t know of a Core function/method to retrieve post meta with the key. That isn’t to say that there definitively isn’t one. There may be. I don’t know everything about WordPress, I just pretend to 🙂 Or maybe it has just slipped my mind right now.

    However, the fourth parameter of update_post_meta is to ensure that you only update the value you want to update in cases where there are multiple keys.

    $prev_value
    (mixed) (optional) The old value of the custom field you wish to change. This is to differentiate between several fields with the same
    key. If omitted, and there are multiple rows for this post and meta
    key, all meta values will be updated.

    You send the previously saved value in that fourth parameter and then only that entry is updated.

    delete_post_meta operates similarly, but with the third parameter not the fourth.

  3. add this code in your functions.php

    function get_mid_by_key( $post_id, $meta_key ) {
        global $wpdb;
        $mid = $wpdb->get_var( $wpdb->prepare("SELECT meta_id FROM $wpdb->postmeta WHERE post_id = %d AND meta_key = %s", $post_id, $meta_key) );
        if( $mid != '' )
        return (int) $mid;
    
        return false;
    }
    

    and then call where ever you want like

    $meta_id = get_mid_by_key( your_post_id, 'your_meta_key' );
    
  4. It is odd that WordPress provides functions to get, update and delete meta via meta_id since v3.3, but as of v3.7 does not have any functions that return the meta id. So, using the core functions from meta.php as reference, I implemented the below functions to be able to retrieve the meta ids along with the values.

    Solution

    Use the custom function below get_post_meta_db() to get the meta key ids and values, then use WordPress’ update_meta() and delete_meta() to manipulate the post meta.

    For example:

    $meta = get_post_meta_db( $post_id, 'my_key', true ); // Returns array( 
    //   'post_id' => 5, 
    //   'meta_id' = 33, 
    //   'meta_key' => 'my_key', 
    //   'meta_value' => 'the_value_for_my_key' );
    update_meta( $meta['meta_id'], $meta['meta_key'], 'new_value' );
    

    Here are the custom function definitions:

    /**
     * Alternative to get_post_meta(), to retrieve meta_ids. @see get_meta_db()
     */
    function get_post_meta_db( $post_id, $meta_key = null, $single = false, $meta_val = null, $output = OBJECT ){
        return get_meta_db( 'post', $post_id, $meta_key, $meta_val, $single, $output );
    }
    
    /**
     * Alternative to get_metadata(). Differences:
     *  - returns every meta field (instead of only meta_values)
     *  - bypasses meta filters/actions
     *  - queries database, bypassing cache
     *  - returns raw meta_values (instead of unserializing arrays)
     *
     * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
     * @param int    $object_id ID of the object metadata is for
     * @param string $meta_key  Optional. Metadata key to retrieve. By default, returns all metadata for specified object.
     * @param mixed  $meta_val  Optional. If specified, will only return rows with this meta_value.
     * @param bool   $single    Optional. If true, returns single row, else returns array of rows.
     * @param string $output    Optional. Any of ARRAY_A | ARRAY_N | OBJECT | OBJECT_K constants. @see wpdb::get_results()
     *
     * @return array Single metadata row, array of rows, empty array if no matches, or false if there was an error.
     */
    function get_meta_db( $meta_type, $object_id = null, $meta_key = null, $meta_val = null, $single = false, $output = OBJECT ){
    
        if( !$meta_type || !$table = _get_meta_table( $meta_type ) ) 
            return false;
    
        // Build query
        global $wpdb;
        $query = $wpdb->prepare( "SELECT * FROM $table", $object_id );
        // Add passed conditions to query
        $where = array();
        if( $object_id = absint( $object_id ) )
            $where[] = $wpdb->prepare( sanitize_key( $meta_type.'_id' ).' = %d', $object_id );
        if( !empty($meta_key) )
            $where[] = $wpdb->prepare( 'meta_key = %s', wp_unslash( $meta_key ) );
        if( null !== $meta_val )
            $where[] = $wpdb->prepare( 'meta_value = %s', maybe_serialize(wp_unslash($meta_val)));
        if( !empty($where) )
            $query .= ' WHERE '.implode(' AND ', $where );
        if( $single )
            $query .= ' LIMIT 1';
    
        $rows = $wpdb->get_results( $query, $output );
    
        if( empty( $rows ) )
            return ( $single ? null : array() );
    
        /* 
        Uncomment this section to have this function unserialize values (like get_metadata())
        NOTE: This can be implemented more efficiently using array_map
        // Unserialize serialized meta_values
        foreach( $rows as &$r ){
            $v =& ($output==ARRAY_A ? $r['meta_value'] : $output==ARRAY_N ? $r[3] : $r->meta_value );
            $v = maybe_unserialize( $v );
        } */
    
        return ( $single ? reset( $rows ) : $rows );
    }
    

    Notes:

    • These functions query the database, so use sparingly!
    • This implementation follows the convention seen in get_metadata_by_mid(), by not using the "get_post_metadata"/"get_user_metadata" pre-filters, and not updating the cache.
    • In my case, I only needed the raw data, so unlike get_metadata_by_mid(), this implementation does not unserialize the meta values.
    • These functions allow metadata to be selected by post_id/user_id, meta key, meta value, or any combination thereof.
  5. The basic idea is that because there may be multiple meta_values for the same meta_key, I would have to know the meta_id in order to reliably update/delete the post meta.

    What you call meta_id here technically does not exist in WordPress. There is the post_id and that is also the id for all the meta (in meta sprak this is the object_id).

    Next to that there is the meta_key and as you rightfully write, as there can be more than one value per each meta_key on the same object_id, there is ambiguity because all this depends on is the order in which the database presents values here – and that only indirect, because meta values also have their cache, the object cache.

    So even you can see a meta_id in the database, it’s not further used only to have a primary key in the db.

    It is not part of the database result that is used within WordPress PHP userland code and therefore it is not part of the meta-cache and therefore never makes it into the meta API functions (because they access the database via the meta cache API).

    Which leads to the final conclusion:

    As you talk about meta_id and it only exists in the database (dbprefix_postmeta table), your question can only mean to delete / update an entry in that table. Just normal SQL operations should suffice.

  6. The WordPress core function has_meta returns associative arrays of meta data. Each meta data array contains the meta_id.

    Return

    (array[]) Array of meta data arrays for the given post ID.

    • ‘…$0’ (array) Associative array of meta data.
      • ‘meta_key’ (string) Meta key.
      • ‘meta_value’ (mixed) Meta value.
      • ‘meta_id’ (string) Meta ID as a numeric string.
      • ‘post_id’ (string) Post ID as a numeric string.

    You can reliably update and delete meta values that share the same meta key using has_meta with update_metadata_by_mid and delete_metadata_by_mid.

    In the OPs example, to update array('myvalue1', 1002) and delete array('myvalue2', 1003):

    $metadata = has_meta( $post_id );
    
    foreach ( $metadata as $item ) {
        // Convert the meta_id numeric string to an integer
        $meta_id    = intval( $item[ 'meta_id' ] );
        $meta_key   = $item[ 'meta_key' ];
        $meta_value = $item[ 'meta_value' ];
    
        if ( $meta_key === 'my_key' && $meta_value === 'myvalue1' ) {
            update_metadata_by_mid( 'post', $meta_id, 'new value' );
        } else if ( $meta_key === 'my_key' && $meta_value === 'myvalue2' ) {
            delete_metadata_by_mid( 'post', $meta_id );
        }
    }
    

    Reference: has_meta is used by WordPress core to populate the form fields in the Custom Fields panel.

Comments are closed.