Как выполнить запрос постов по частичному мета-ключу?
У меня есть функция, которая сохраняет статус "лайка" для поста как мета-данные. Я хочу связать этот "лайк" с пользователем, который его поставил, поэтому я создал пользовательское поле "like_status_{user_id}" (где {user_id} - это id текущего авторизованного пользователя), которое сохраняется как 0 или 1. Таким образом, для поста с несколькими "лайками" в базе данных будет несколько мета-значений, настроенных следующим образом:
'meta_key' = 'like_status_0'
'meta_value' = 1
'meta_key' = 'like_status_2'
'meta_value' = 1
'meta_key' = 'like_status_34'
'meta_value' = 1
...и так далее.
Потенциально на конкретном посте могут быть тысячи лайков. Как мне выполнить запрос, который показал бы, лайкнул ли кто-то еще этот пост?
Я думал о чем-то вроде этого:
$query = new WP_Query(array(
'meta_key' => 'like_status_{user_id}',
'meta_value' => 1,
));
Я пытаюсь отправить уведомление всем, кто лайкнул пост, когда кто-то другой ставит лайк этому посту... что-то вроде: "Эй, кто-то еще лайкнул пост, который понравился вам. Вы должны это проверить!" Но мне нужен способ узнать, лайкнул ли кто-то еще этот пост, и если да, то кто именно, чтобы я мог уведомить их.
Если это невозможно, не могли бы вы предложить лучший способ хранения этих данных как post_meta, сохраняя при этом эффективность быстрого обновления статуса лайка отдельного пользователя на посте?

Довольно сложно дать конкретный ответ на ваш вопрос. Первая часть проста. Недавно я делал что-то похожее на stackoverflow
Мета-ключи сравниваются и должны точно совпадать. WP_Query
не предоставляет простого параметра для изменения этого поведения, но мы можем ввести его сами и затем изменить условие posts_where
, чтобы использовать сравнение LIKE
для мета-ключей.
ФИЛЬТР
Это базовый фильтр, адаптируйте его по необходимости.
add_filter( 'posts_where', function ( $where, \WP_Query $q )
{
// Проверяем наш пользовательский параметр запроса
if ( true !== $q->get( 'wildcard_on_key' ) )
return $where;
// Модифицируем условие
$where = str_replace( 'meta_key =', 'meta_key LIKE', $where );
return $where;
}, 10, 2 );
Как видите, фильтр срабатывает только при установке нашего нового параметра wildcard_on_key
в true
. После проверки мы просто заменяем оператор =
на LIKE
.
Отмечу, что сравнения с LIKE
по своей природе более ресурсоемки, чем другие виды сравнений.
ЗАПРОС
Вы можете запросить записи следующим образом, чтобы получить все посты с мета-ключами вида like_status_{user_id}
:
$args = [
'wildcard_on_key' => true,
'meta_query' => [
[
'key' => 'like_status_',
'value' => 1,
]
]
];
$query = new WP_Query( $args );
ДРУГОЙ ВОПРОС
Пользовательские поля не влияют на производительность, об этом можно прочитать в моем посте здесь. Однако меня беспокоит, что у вас может быть сотни или тысячи лайков на один пост. Это может ударить по производительности при получении и кэшировании такого объема данных. Также это может засорить базу данных огромным количеством ненужных пользовательских полей, что усложнит поддержку.
Я не сторонник хранения сериализованных данных в пользовательских полях, так как по ним нельзя искать или сортировать. Вместо этого я предлагаю хранить все ID пользователей в массиве под одним пользовательским полем. Вы можете просто обновлять этот массив ID при добавлении лайка. Получить данные поля и работать с массивом ID просто - используйте get_post_meta()
.
Обновление пользовательского поля также просто. Для этого используйте update_post_meta()
. Не знаю, как вы создаете свои поля, но update_post_meta()
точно вам пригодится.
Если нужно отправлять email или уведомления при обновлении поля, доступны следующие хуки (см. update_metadata()
для контекста):
ЗАКЛЮЧЕНИЕ
Перед тем как использовать сериализацию, убедитесь, что вам не потребуется сортировка или поиск по этим данным.

Спасибо за объяснение производительности post_meta! Очень полезно.

Этот ответ должен быть принятым, всегда лучше использовать фильтры вместо кастомных запросов. Также обратите внимание, что если вы используете get_posts вместо WP_Query, нужно передать suppress_filters => false, иначе фильтр не сработает. Для выполнения LIKE по мета-ключу также необходимо добавить % перед и/или после ключа в массиве, в зависимости от типа поиска LIKE, который вам нужен.

К сожалению, вы не можете выполнить meta_query
с использованием сравнения LIKE
для значения meta_key
при работе с WP_Query
. Я уже проходил этот путь...
Вместо этого у вас есть несколько других вариантов, если вы хотите сохранить статусы лайков как метаданные записи, а не метаданные пользователя или метаданные в пользовательской таблице.
Вариант 1
- не требует изменения структуры ваших метаданных
- использует класс
wpdb
для выполнения пользовательского запроса
Пример:
//когда пользователь лайкает запись...
$current_user_id = get_current_user_id();
add_post_meta($current_user_id, "like_status_{$current_user_id}", 1, false);
//позже в запросе...
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){
//применяем ко всем пользователям, кроме того, кто поставил лайк
if ( $notify_user_id !== $current_user_id ) {
//логика уведомления здесь...
}
});
Примечание: логику можно упростить дальше, если хотите.
Вариант 2
- требует изменения структуры ваших метаданных
- требует сохранения ID пользователя как значения метаданных
- позволяет использовать
WP_Query
вместе сmeta_query
Вариант 2 требует изменения мета-ключа с like_status_{user_id}
на что-то универсальное, например, like_status
или liked_by_user_id
, где вместо хранения значения 1
для ключа вы сохраняете ID пользователя как значение.
//когда пользователь лайкает запись...
$current_user_id = get_current_user_id();
add_post_meta($current_user_id, "liked_by_user_id", $current_user_id, false);
//позже в запросе
$args = array(
'post_type' => 'post', //или любой другой тип записи
'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){
//уведомляем всех пользователей, кроме того, кто поставил лайк
if ( $notify_user_id !== $current_user_id ) {
//логика уведомления здесь...
//получаем пользователя, например: $user = get_user_by('id', $notify_user_id);
}
});
});

Пожалуйста, обновите принятый ответ, чтобы он отражал последние возможности WP, или отметьте ответ @K.Tromp как самый актуальный

Если в дальнейшем вы захотите расширить функционал, добавив более детальную статистику, дополнительные возможности и т.д., то еще одним вариантом может быть использование: пользовательских таблиц
плюсы: Полностью адаптированы под ваши нужды и могут быть проиндексированы для лучшей производительности.
минусы: Требуют больше работы
Также возможен обходной путь с использованием пользовательской таксономии, что может обеспечить лучшую производительность запросов по сравнению с запросами к метаполям записей, благодаря тому, как индексируются основные таблицы.
Я пытаюсь отправить уведомление всем, кто поставил лайк записи, когда кто-то еще лайкает эту запись... что-то вроде: "Эй, кто-то еще лайкнул запись, которая понравилась вам. Возможно, вам стоит посмотреть ее снова!" Но мне нужен способ узнать, лайкал ли кто-то еще эту запись, и если да, то кто эти пользователи, чтобы я мог уведомить их.
Не уверен, какие именно уведомления вы имеете в виду, но это может быстро стать громоздким.
Пример: Пользователь, который поставил лайк ~1000 записям, и каждая запись получает ~1000 лайков, тогда в очереди оказывается 1 млн уведомлений только для этого пользователя! Если это email-уведомления, то хостинг-провайдер может быть недоволен, а пользователь сойдет с ума. Это также может быть дорого при использовании стороннего сервиса email-рассылок.

На самом деле, я отправляю уведомления только один раз для каждого человека на пост. Так что это меньше, чем кажется — хотя все равно много. Причина, по которой я пытаюсь использовать встроенные таблицы, заключается в том, что в будущем я хотел бы иметь возможность использовать стандартный WP REST API в реальном приложении с этими данными.

Согласно документации WP_Meta_Query, вы можете использовать аргумент compare
в аргументе meta_query
WP_Query. Однако вы можете сравнивать только по value
, а не по key
, поэтому вам, возможно, стоит пересмотреть структуру.
Аргумент like
будет выглядеть так:
$arguments = array(
'meta_query' => array(
array(
'key' => 'foo',
'value' => 'ba',
'compare' => 'LIKE'
)
)
);
$query = new WP_Query($arguments);
Учитывая, что вы не можете выполнить поиск по key
с условием 'LIKE', я предлагаю добавить понравившиеся записи в метаданные пользователя и выполнить поиск пользователей, которым понравилась эта запись, с помощью WP_User_Query:
$arguments = array(
'meta_query' => array(
array(
'key' => 'liked_post',
'value' => '<post_id>'
)
)
);
$users = new WP_User_Query($arguments);
