Wordpress - How do I query for posts by partial meta key?
It is quite difficult to concretely answer your question. The first part is easy though. I recently did something similar on stackoverflow
Meta keys are are compared and match exactly. WP_Query
have no means to adjust this behavior with a simple parameter, but we can always introduce one ourselves and then adjust the posts_where
clause to do a LIKE
comparison on meta keys.
THE FILTER
This is just a basic filter, adjust it as needed.
add_filter( 'posts_where', function ( $where, \WP_Query $q )
{
// Check for our custom query var
if ( true !== $q->get( 'wildcard_on_key' ) )
return $where;
// Lets filter the clause
$where = str_replace( 'meta_key =', 'meta_key LIKE', $where );
return $where;
}, 10, 2 );
As you can see, the filter is only fired when we set our new custom parameter, wildcard_on_key
to true
. When this checks out, we simply change the =
comparator to the LIKE
comparator
Just a note on this, LIKE
comparisons are inherently more expensive to run that other comparisons
THE QUERY
You can simply query your posts as follow to get all posts with meta keys like_status_{user_id}
$args = [
'wildcard_on_key' => true,
'meta_query' => [
[
'key' => 'like_status_',
'value' => 1,
]
]
];
$query = new WP_Query( $args );
OTHER QUESTION
Custom fields does not have impact on performance, you can read my post on this subject here. I am however troubled by that you say each post can have hundreds or thousands of likes. This can hit you on performance getting and caching such a large amount of custom field data. It can also clog your db with a huge amount of unnecessary custom field data which makes it quite hard to maintain.
I am not a very big fan of storing serialized data in custom fields as one cannot search or order by serialized data. I would however suggest storing all the user ID's in an array under one custom field. You can simply just update the array with the user ID when a user like a post. Getting the custom field data and looping over the array of ID's and doing something with the ID's are easy. Just have a look at get_post_meta()
Updating a custom field is also easy. For that, you will need to look into update_post_meta()
, I do not know how you create your custom fields, but update_post_meta()
is definitely something you would want to use.
If you need to send emails or push notifications when a custom field is updated, you have the following hooks available to work with. (See update_metadata()
for context)
update_postmeta
updated_{$meta_type}_meta
updated_postmeta
update_{$meta_type}_meta
update_{$meta_type}_metadata
CONCLUSION
Just before I post this, again, before you go the serialized route, make sure that you would not need to sort by the sorted data or search for particular data inside the serialized data.
Since wordpress 5.1 it is possible now to use meta query like:
Unfortunately you cannot perform a meta_query
using a LIKE
comparison on the meta_key
value when using WP_Query
. I've been down this road...
Instead you have a couple other options if you want to maintain like status relationships as post meta and not user meta and or meta in a custom table.
Option 1
- requires no modification of your meta schema
- uses
wpdb
class to perform a custom query
Example:
//when a user likes a post...
$current_user_id = get_current_user_id();
add_post_meta($current_user_id, "like_status_{$current_user_id}", 1, false);
//later in the request...
global $wpdb;
$results = $wpdb->get_results(
"
SELECT meta_key
FROM {$wpdb->prefix}postmeta
WHERE meta_key
LIKE 'like_status_%'
",
ARRAY_N
);
$results = array_map(function($value){
return (int) str_replace('like_status_', '', $value[0]);
}, $results);
array_walk($results, function($notify_user_id, $key){
//apply to all users except the user who just liked the post
if ( $notify_user_id !== $current_user_id ) {
//notify logic here...
}
});
Note: logic could be simplified further if you wish.
Option 2
- requires you change your meta schema
- requires you store the user id as the meta value
- allows you to use
WP_Query
along withmeta_query
Option 2 requires that you change your meta key from like_status_{user_id}
to something universal such as like_status
or liked_by_user_id
where in turn instead of storing the value of 1
against the key, you instead store the user's id as the value.
//when a user likes a post...
$current_user_id = get_current_user_id();
add_post_meta($current_user_id, "liked_by_user_id", $current_user_id, false);
//later in the request
$args = array(
'post_type' => 'post', //or a post type of your choosing
'posts_per_page' => -1,
'meta_query' => array(
array(
'key' => 'liked_by_user_id',
'value' => 0,
'type' => 'numeric'
'compare' => '>'
)
)
);
$query = new WP_Query($args);
array_walk($query->posts, function($post, $key){
$user_ids = get_post_meta($post->ID, 'liked_by_user_id');
array_walk($user_ids, function($notify_user_id, $key){
//notify all users except the user who just like the post
if ( $notify_user_id !== $current_user_id ) {
//notify logic here...
//get user e.g. $user = get_user_by('id', $notify_user_id);
}
});
});