WordPress Meta Query Arrays

2020-02-09 05:35发布

问题:

I'm building a way form my users to "like" a post.

I'll have an array of users that have liked posts:

$user_ids = array(60, 61, 62, 63);

And then a post will have a post_meta that saves the user ID when the "like" button is clicked on a post, like so:

// Build array of user IDs that have liked this post
$likes = array(61, 62);

// Save array
update_post_meta($post->ID, likes, $likes);

So how can I return all posts liked by the users. This is how I'm doing it currently, which doesn't work.

$args = array(
    'post_type'         => 'post',
    'post_status'       => 'publish',
    'posts_per_page'    => -1,
    'orderby'           => 'rand',
    'meta_query'        => array(
        'relation'  => 'AND',
        array(
            'key'       => 'likes',
            'value'     => $user_ids,
            'compare'   => 'IN'
        )
    )
);
$posts = get_posts($args);

This should return all posts that have '60, 61, 62, 63' saved in the posts 'likes' meta. But it returns 0 results.

Now, I think this is because WordPress serializes arrays when they are saved in a post_meta.

So, how do I a do a query like this? Is it even possible? Maybe I have to use the $wpdb class.

Thanks!

回答1:

Try this one with WP_Query no need of relation just use the compare part with IN

 $user_ids = array(60, 61, 62, 63);
 $args = array(
   'post_type' => 'post',
   'meta_key' => 'likes',
   'post_status'       => 'publish',
   'posts_per_page'    => -1,
   'orderby'           => 'rand',       
   'order' => 'ASC',
   'meta_query' => array(
       array(
           'key' => 'likes',
           'value' => $user_ids, //array
           'compare' => 'IN',
       )
   )
 );
 $query = new WP_Query($args);

See the given example above "Time Parameters" Heading WP_Query

OR by get_posts try this

$args = array(
    'post_type'         => 'post',
    'post_status'       => 'publish',
    'posts_per_page'    => -1,
    'orderby'           => 'rand',
    'meta_query'        => array(

        array(
            'key'       => 'likes',
            'value'     => $user_ids,
            'compare'   => 'IN'
        )
    )
);
$posts = get_posts($args);

OR by a custom query you can do as follows

global $wpdb;

$liked_posts=$wpdb->get_results("SELECT * FROM `wp_posts` WHERE 
post_type='post' AND post_status ='publish' AND ID
 IN(
SELECT post_id FROM `wp_postmeta` WHERE meta_key='likes' 
AND meta_value IN (".join(',',$user_ids).")
) ORDER BY RAND()");

Also don't store array of ids in one row instead loop through ids array and normalize your likes data manually Don't serialize data into a database field. That's what Database_normalization is for and insert each id in new row because

Wordpress stores the meta values as strings. When you pass update_post_meta an array, it automatically converts it to a string. What you'll need to do is unserialize it when you attempt to read the data but in mysql you can't unserialize column data in the query because mysql doesn't about the serialization of php

$likes = array(61, 62);

foerach($likes as $l){

update_post_meta($post->ID, "likes", $l);

}

And below is the query you have asked for in bounty comments

If a $wpdb answer is given, then please elaborate on how to factor in category (include and exclude), and ignore posts by an array of IDs

$liked_posts=$wpdb->get_results("
SELECT * FROM `wp_posts` wp
INNER JOIN `wp_term_relationships` wtr ON (wp.`ID`=wtr.`object_id`)
INNER JOIN  `wp_term_taxonomy` wtt ON (wtr.`term_taxonomy_id` =wtt.`term_taxonomy_id`)
WHERE  wp.post_type='post' AND wp.post_status ='publish' AND wp.ID
 IN(
SELECT post_id FROM `wp_postmeta` WHERE meta_key='likes' 
AND meta_value IN (".join(',',$user_ids).")
)  AND wp.ID NOT IN (100,101,102)
AND wtt.`term_id` IN(1,2,3) AND wtt.`term_id` NOT IN (4,5,6,)    
ORDER BY RAND() ");

I have used the INNER JOINs on wp_term_relationships and wp_term_taxonomy the table wp_term_relationshipsstores the relation of posts and the taxonomy of category and the table wp_term_taxonomy have the taxonomy of the category and also the category id

Here is the part which covers the

1. category (include and exclude)

AND wtt.`term_id` IN(1,2,3) AND wtt.`term_id` NOT IN (4,5,6,) 

2. ignore posts by an array of IDs

AND wp.ID NOT IN (100,101,102) 
or $postids =array(100,101,102);
AND wp.ID NOT IN (".join(',',$postids).")


回答2:

You're going to run into problems with your current approach because, as you note, the ids are saved as serialized arrays in a single field. An easier approach would be to save each like as a single postmeta entry. So meta_key = "like" and meta_value = user ID.

So for storing the likes switch from update_post_meta to add_post_meta:

/** The fourth parameter, false, tells WP that there can be multiple
  * post_meta entries with the same meta_key. It's false by default, just here
  * here for reference
  */
add_post_meta($post->ID, 'like', $user_id, false);

Then to get posts liked by a specific user you would just do:

$liked_posts = $wpdb->get_results("SELECT post_id FROM {$wpdb->prefix}postmeta 
              WHERE meta_key = 'like' AND meta_value = $user_id");

To get results for a number of users at once, like user ID 4, 23, 39, etc.

$liked_posts_by_user = $wpdb->get_results("SELECT meta_value as user_id, 
         GROUP_CONCAT(post_id) as posts FROM {$wpdb->prefix}wp_postmeta 
         WHERE meta_value IN('22','23','24') GROUP BY user_id

That will give you results like:

  user_id    posts
   23         1
   25         2,40,3

So just replace the IN portion of the query with your array of user ids.



回答3:

I had a similar problem and i solved it using the serialize(), you can use ir in case that the vale you are working with is an integer here´s my example:

    $args = array(
    'meta_query' => array(
        array(
            'key' => 'my_meta_key',
            'value' => serialize(strval($my_id)),
            'compare' => 'LIKE'
        )
    )
);
$posts = get_posts( $args );

and that´s all, you hace your meta query ready.



回答4:

Your query is right. But as you are storing user ids as a string your query wont work. Save user id a single meta value for post.

add_post_meta($post->ID, 'like', $user_id, false);

and replace 'compare' => 'IN' with 'compare' => 'LIKE'