Как выполнить запрос постов по частичному мета-ключу?

26 мар. 2016 г., 05:00:10
Просмотры: 23.1K
Голосов: 11

У меня есть функция, которая сохраняет статус "лайка" для поста как мета-данные. Я хочу связать этот "лайк" с пользователем, который его поставил, поэтому я создал пользовательское поле "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, сохраняя при этом эффективность быстрого обновления статуса лайка отдельного пользователя на посте?

0
Все ответы на вопрос 5
3
12

Довольно сложно дать конкретный ответ на ваш вопрос. Первая часть проста. Недавно я делал что-то похожее на 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() для контекста):

ЗАКЛЮЧЕНИЕ

Перед тем как использовать сериализацию, убедитесь, что вам не потребуется сортировка или поиск по этим данным.

26 мар. 2016 г. 09:09:10
Комментарии

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

codescribblr codescribblr
26 мар. 2016 г. 20:25:14

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

Earle Davies Earle Davies
31 июл. 2016 г. 20:10:30

А как бы вы отфильтровали, если хотите запросить посты, но ИСКЛЮЧИТЬ все посты, имеющие мета-ключ с определенным префиксом? (например, исключить все посты с мета-полем LIKE 'my_prefix_' ?

gordie gordie
1 апр. 2017 г. 18:47:42
1

Начиная с WordPress 5.1 стало возможным использовать мета-запросы следующим образом: Пример использования мета-запроса в WordPress

27 февр. 2019 г. 09:59:05
Комментарии

Экранирование подчеркиваний, похоже, является проблемой при использовании этого метода, но в остальном выглядит довольно хорошо. Спасибо, что обратили на это внимание.

Jake Jake
24 апр. 2019 г. 00:20:40
4

К сожалению, вы не можете выполнить 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);
            
        }

    });

});
26 мар. 2016 г. 09:11:51
Комментарии

С версии 5.1, ознакомьтесь с моим ответом ниже

K. Tromp K. Tromp
5 нояб. 2019 г. 00:13:14

@K.Tromp Ура!

Adam Adam
5 нояб. 2019 г. 12:50:55

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

zumek zumek
5 мая 2020 г. 05:23:40

Устарело, теперь вы можете.

Michael Rogers Michael Rogers
5 янв. 2021 г. 00:20:43
1

Если в дальнейшем вы захотите расширить функционал, добавив более детальную статистику, дополнительные возможности и т.д., то еще одним вариантом может быть использование: пользовательских таблиц

  • плюсы: Полностью адаптированы под ваши нужды и могут быть проиндексированы для лучшей производительности.

  • минусы: Требуют больше работы

Также возможен обходной путь с использованием пользовательской таксономии, что может обеспечить лучшую производительность запросов по сравнению с запросами к метаполям записей, благодаря тому, как индексируются основные таблицы.

Я пытаюсь отправить уведомление всем, кто поставил лайк записи, когда кто-то еще лайкает эту запись... что-то вроде: "Эй, кто-то еще лайкнул запись, которая понравилась вам. Возможно, вам стоит посмотреть ее снова!" Но мне нужен способ узнать, лайкал ли кто-то еще эту запись, и если да, то кто эти пользователи, чтобы я мог уведомить их.

Не уверен, какие именно уведомления вы имеете в виду, но это может быстро стать громоздким.

Пример: Пользователь, который поставил лайк ~1000 записям, и каждая запись получает ~1000 лайков, тогда в очереди оказывается 1 млн уведомлений только для этого пользователя! Если это email-уведомления, то хостинг-провайдер может быть недоволен, а пользователь сойдет с ума. Это также может быть дорого при использовании стороннего сервиса email-рассылок.

26 мар. 2016 г. 13:18:18
Комментарии

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

codescribblr codescribblr
26 мар. 2016 г. 20:23:06
0
-1

Согласно документации 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);
26 мар. 2016 г. 05:39:55