Вопрос

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

<p> 
    <label for="latitude">Latitude:</label><br /> 
    <input type="text" id="latitude" name="coordinates[latitude]" class="full-width" value="" /> 
</p> 
<p>     
    <label for="longitude">Longitude:</label><br /> 
    <input type="text" id="longitude" name="coordinates[longitude]" class="full-width" value="" /> 
</p>

По какой -то причине мне понравилась идея иметь единственную запись в постмета для каждого метабокс. На save_post Крюк, я сохраняю данные так:

update_post_meta($post_id, '_coordinates', $_POST['coordinates']);

Я сделал это, потому что у меня есть три метабокс, и мне нравится иметь 3 значения постмета для каждого поста; Тем не менее, теперь я осознал потенциальную проблему с этим. Возможно, я захочу использовать wp_query, чтобы вытащить только определенные посты, основанные на этих значениях мета. Например, я могу захотеть получить все посты, которые имеют значения широты выше 50. Если у меня были эти данные в базе данных индивидуально, возможно, используя ключ latitude, Я бы сделал что -то вроде:

$args = array(
    'post_type' => 'my-post-type',
    'meta_query' => array(
        array(
            'key' => 'latitude',
            'value' => '50',
            'compare' => '>'
        )
    )
 );
$query = new WP_Query( $args );

Поскольку у меня есть широта как часть _coordinates Postmeta, это не сработает.

Итак, мой вопрос: есть ли способ использовать meta_query Чтобы запросить сериализованный массив, как в этом сценарии?

Это было полезно?

Решение

Нет, это невозможно, и может быть даже опасным.

Я настоятельно рекомендую вам не использовать ваши данные и изменить вашу процедуру сохранения. Что -то похожее на это должно преобразовать ваши данные в новый формат:

$args = array(
    'post_type' => 'my-post-type',
    'meta_key' => '_coordinates',
    'posts_per_page' => -1
 );
$query = new WP_Query( $args );
if($query->have_posts()){
    while($query->have_posts()){
        $query->the_post();
        $c = get_post_meta($post->id,'_coordinates',true);
        add_post_meta($post->ID,'_longitude',$c['longitude']);
        add_post_meta($post->ID,'_latitude',$c['latitude']);
        delete_post_meta($post->ID,'_coordinates',$c);
    }
}

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

Если вам нужно хранить несколько долгот и несколько широт, вы можете хранить несколько пост -мета с одинаковым именем. Просто используйте третий параметр get_post_meta, и это вернет их все как массив

Почему вы не можете запрашивать сериализованные данные?

MySQL рассматривает это как строку и не может разбить его на структурированные данные. Разбить его на структурированные данные именно то, что делает приведенный выше код

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

Помимо затрат на частичный поиск строкости, запросы после метама являются медленными, а сериализованные данные могут меняться в зависимости от таких вещей, как продолжительность содержания, делая поиск невероятно дорогим, если не невозможным в зависимости от значения, которое вы ищете

Примечание о хранении записей/объектов/объектов в качестве сериализованных объектов в мета

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

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

Безопасность и сериализованные объекты

Хранение сериализованных объектов PHP через serialize Функция может быть опасной, что прискорбно, когда передача объекта в WordPress будет означать, что он сериализуется. Это связано с тем, что, когда объект де-сериализован, создается объект, и все его методы и конструкторы пробуждения выполняются. Это может не показаться большим делом, пока пользователю не удастся проникнуть тщательно продуманным вводом, что приведет к удаленному выполнению кода, когда данные считываются из базы данных и де-эриализованы WordPress.

Этого можно избежать, используя JSON, что также облегчает запросы, но гораздо проще/быстрее просто хранить данные правильно и избежать структурированных сериализованных данных с самого начала.

Другие советы

Я также сталкиваюсь с этой ситуацией. Вот что я сделал:

$args = array(
    'post_type' => 'my-post-type',
    'meta_query' => array(
        array(
            'key' => 'latitude',
            'value' => sprintf(':"%s";', $value),
            'compare' => 'LIKE'
        )
    )
);

Надеюсь, это поможет

Вы действительно собираетесь потерять возможность запрашивать свои данные каким -либо эффективным образом при сериализации записей в базу данных WP.

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

Вместо этого сохраните сериализацию для данных, которые вы не собираетесь запрашивать в таком характере, а вместо этого получит доступ только в прямом эфире с помощью прямого вызова API WP get_post_meta() - Из этой функции вы также можете распаковать сериализованную запись, чтобы получить доступ к его свойствам массива.

Фактически присвоено значение истинный как в;

$meta = get_post_meta( $post->ID, 'key', true );

Вернут данные в качестве массива, доступный для вас, чтобы выполнить, согласно обычно.

Вы можете сосредоточиться на других оптимизациях базы данных/сайтов, таких как кэширование, MINISICAN CSS и JS и использование таких услуг в качестве CDN, если вам необходимо. Называть лишь несколько .... WordPress Codex - хорошая отправная точка, чтобы раскрыть больше по этой теме: ЗДЕСЬ

Я только что имел дело с сериализованными полями и мог запросить их. Не используя meta_query, а используя запрос SQL.

global $wpdb; 

$search = serialize('latitude').serialize(50);

$query = $wpdb->prepare("SELECT `post_id`
FROM `wp_postmeta`
WHERE `post_id` IN (SELECT `ID` FROM `wp_posts` WHERE `post_type` = 'my-post-type')
AND `meta_key` = '_coordinates'
AND `meta_value` LIKE '%s'",'%'.$search.'%');

$ids = $wpdb->get_col($query);

$args = array(
    'post__in' => $ids
    'post_type' => 'team' //add the type because the default will be 'post'
);

$posts = get_posts($args);

Запрос сначала ищет сообщение с соответствующим post_type, поэтому количество записей WP_Postmeta будет меньше для фильтрации. Затем я добавил оператор, чтобы уменьшить строки, фильтруя на meta_key

Идентификаторы заканчиваются в массиве по мере необходимости для get_posts.

Пса MySQL v5.6 или выше необходим для хорошей подпрофессиональной производительности

Этот пример действительно помог мне. Это специально для плагина S2 Members (который сериализует метаданные пользователя). Но это позволяет вам запросить часть сериализованного массива в Meta_Key.

Он работает, используя функцию Regexp MySQL.

Здесь Источник

Вот код, который запрашивает всех пользователей, живущих в США. Я легко модифицировал его, чтобы запросить одну из моих пользовательских областей регистрации, и он работал в кратчайшие сроки.

  <?php
global $wpdb;
$users = $wpdb->get_results ("SELECT `user_id` as `ID` FROM `" . $wpdb->usermeta . 
          "` WHERE `meta_key` = '" . $wpdb->prefix . "s2member_custom_fields' AND 
           `meta_value` REGEXP '.*\"country_code\";s:[0-9]+:\"US\".*'");
if (is_array ($users) && count ($users) > 0)
    {
        foreach ($users as $user)
            {
                $user = /* Get full User object now. */ new WP_User ($user->ID);
                print_r($user); /* Get a full list of properties when/if debugging. */
            }
    }
?>

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

array(37,87);

хранится как сериализованный массив, как это

a:2:{i:0;i:37;i:1;i:87;}

Обратите внимание i:0 как первая позиция массива и i:37 как первое значение. Шаблон такой же. Но давайте перейдем к решениям


1) Решение REGEXP

Это решение работает для меня независимо от значения мета, сохраняемого как строка или число / идентификатор. Однако он использует REGEXP, что не так быстро, как использование LIKE

$args = array(
    'post_type' => 'my-post-type',
    'meta_query' => array(
        array(
            'key' => 'latitude',
            'value' => '\;i\:' . $value . '\;|\"' . $value . '\";',
            'compare' => 'REGEXP'
        )
    )
);

2) как решение

Я не уверен в разнице в производительности, но это решение, которое использует LIKE а также работает как для числа, так и для строк

 $args = array(
        'post_type' => 'my-post-type',
        'meta_query' => array(
            'relation' => 'OR',
            array(
                'key' => 'latitude',
                'value' => sprintf(':"%s";', $value),
                'compare' => 'LIKE'
            ),
            array(
                'key' => 'latitude',
                'value' => sprintf(';i:%d;', $value),
                'compare' => 'LIKE'
            )
        )
    );

После прочтения кучу советов по запуску WP_Query Фильтрация по сериализованным массивам, вот как я наконец сделал: создав массив запятых значений, используя Improde в сочетании с $wpdb Пользовательский SQL -запрос с использованием FIND_IN_SET для поиска разделенного списка запятой для запрошенного значения.

(Это похоже на ответ Томаса, но это немного менее интенсивно для запроса SQL)

1. В функциях.php:

В вашем файле functions.php (или где бы вы ни настраивали мета -поле) в yourname_save_post() использование функции

update_post_meta($post->ID, 'checkboxArray', implode(",", $checkboxArray)); //adding the implode

Чтобы создать массив, содержащий запятую, разделенные значения.

Вы также захотите изменить свою выходную переменную в yourname_post_meta() Административная мета -ящик для строительства

$checkboxArray = explode(",", get_post_custom($post->ID)["checkboxArray"][0]); //adding the explode

2. В файле PHP шаблона:

Тест: если вы запускаете get_post_meta( $id ); Тебе следует увидеть checkboxArray в качестве массива, содержащего ваши запятые, разделенные значения вместо сериализованного массива.

Теперь мы строим наш пользовательский запрос SQL, используя $wpdb.

global $wpdb;

$search = $post->ID;

$query = "SELECT * FROM wp_posts
          WHERE FIND_IN_SET( $search, (
              SELECT wp_postmeta.meta_value FROM wp_postmeta
              WHERE wp_postmeta.meta_key = 'blogLocations'
              AND wp_postmeta.post_id = wp_posts.ID )
          )
          AND ( wp_posts.post_type = 'post' )
          AND ( wp_posts.post_status = 'publish' );";

$posts = $wpdb->get_results($query);

foreach ($posts as $post) {
    //your post content here
}

Обратите внимание на FIND_IN_SET, вот где происходит магия.

Теперь ... так как я использую SELECT * это возвращается Все данные о публикациях и внутри foreach Вы можете отразить то, что вы хотите от этого (сделайте print_r($posts); Если вы не знаете, что включено. Он не настраивает «цикл» для вас (я предпочитаю это таким образом), но его можно легко изменить, чтобы настроить петлю, если вы предпочитаете (посмотрите на setup_postdata($post); В кодексе вам, вероятно, нужно изменить SELECT * Чтобы выбрать только идентификаторы публикации и $wpdb->get_results к правильному $wpdb Тип - см. Кодекс для $wpdb Также для получения информации о что предмет).

Whelp, это потребовалось немного усилий, но с тех пор wp_query не поддерживает делать 'compare' => 'IN' Сериализованные или отдельные запятые

Надеюсь, это кому -то поможет.

Если вы используете like Оператор сравнения в вашем мета -запросе он должен работать нормально, чтобы заглянуть в сериализованный массив.

$wp_user_search = new WP_User_Query(array(
    'meta_query' => array(
        array(
            'key'     => 'wp_capabilities',
            'value'   => 'subscriber',
            'compare' => 'not like'
            )
        )
    )
);

Результаты:

[query_where] => WHERE 1=1 AND (
  ( wp_usermeta.meta_key = 'wp_capabilities' 
  AND CAST(wp_usermeta.meta_value AS CHAR) NOT LIKE '%subscriber%' )

Если мои метаданные являются типом массива, я использую этот метод для запроса Meta:

$args = array(
    'post_type' => 'fotobank',
    'posts_per_page' => -1,
    'meta_query' => array(
            array(
                   'key' => 'collections',
                   'value' => ':"'.$post->ID.'";',
                   'compare' => 'LIKE'
            )
     )
);
$fotos = new WP_Query($args);

Мне любопытно о ответах выше, где meta_query нацелены на ключ latitude вместо _coordinates. Анкет Пришлось пойти и проверить, если это действительно возможно в мета -запросах, чтобы нацелиться на конкретный ключ внутри сериализованного массива. :)

Это, очевидно, было не так.

Итак, обратите внимание, что правильный ключ к цели - это _coordinates вместо latitude.

$args = array(
     'post_type' => 'my-post-type',
     'meta_query' => array(
         array(
             'key' => '_coordinates',
             'value' => sprintf(':"%s";', $value),
             'compare' => 'LIKE'
         )
     )
 );

ЗАМЕТКИ:

  1. Этот подход делает возможным только нацеливаться на точные совпадения. Так что вещи вроде Все широты более 50 невозможно.

  2. Чтобы включить подстроки, можно использовать 'value' => sprintf(':"%%%s%%";', $value),. Анкет (не тестировал)

У меня такой же вопрос. Может вам нужен параметр «Тип»? Проверьте этот связанный вопрос:Пользовательский полевой запрос - значение мета - это массив

Возможно, попробуйте:

    $args = array(
    'post_type' => 'my-post-type',
    'meta_query' => array(
        array(
            'key' => 'latitude',
            'value' => '50',
            'compare' => '>',
            'type' => 'numeric'
        )
    )
    );

Я столкнулся с чем -то подобным, используя плагин Magic Fields. Это может сделать свое дело

$values_serialized = serialize(array('50'));
$args = array(
    'post_type' => 'my-post-type',
    'meta_query' => array(
        array(
            'key' => 'latitude',
            'value' => $values_serialized,
            'compare' => '>'
        )
    )
);
Лицензировано под: CC-BY-SA с атрибуция
Не связан с wordpress.stackexchange
scroll top