Как обновить значение update_post_meta в виде массива

19 окт. 2016 г., 12:00:56
Просмотры: 59.1K
Голосов: 9

Я создал новое произвольное поле в элементах моего навигационного меню как множественный выбор опций, используя update_post_meta() следующим образом

function YPE_update_custom_fields($menu_id, $menu_item_db_id, $menu_item_data) {
    if (is_array($_REQUEST['menu-item-content-multiple'])) {
        update_post_meta($menu_item_db_id, '_menu_item_content_multiple', $_REQUEST['menu-item-content-multiple'][$menu_item_db_id]);
    }
}
add_action('wp_update_nav_menu_item', 'YPE_update_custom_fields', 10, 3);

function YPE_setup_custom_fields($item) {
    $item->content_multiple = get_post_meta($item->ID, '_menu_item_content_multiple', true);
    return $item;
}
add_filter('wp_setup_nav_menu_item', 'YPE_setup_custom_fields');

Затем я добавил новое поле в файл Walker_Nav_Menu_Edit следующим образом

ОТРЕДАКТИРОВАНО

<?php
$select_options = array (
    'key_1' => 'вариант1', 
    'key_2' => 'вариант2', 
    'key_3' => 'вариант3'
); 
?>
<p class="field-content-multiple description description-thin">
<label for="edit-menu-item-content-multiple-<?php echo $item_id; ?>">
    <?php _e( 'Множественное содержимое' ); ?><br />
    <select name="menu-item-content-multiple[<?php echo $item_id; ?>][]" id="edit-menu-item-content-multiple-<?php echo $item_id; ?>" class="widefat code edit-menu-item-content-multiple" multiple="multiple">
            <?php foreach ($select_options as $key => $value): ?>
                <option value="<?php echo $key; ?>"  <?php echo selected(in_array($key, $item->content_multiple)); ?>><?php echo $value;?></option>
            <?php endforeach ?>
    </select>
</label>
</p>

Мой код работает с одиночным списком выбора без каких-либо проблем, но когда я использую in_array() в случае множественного выбора, возникает следующая ошибка

Warning: in_array() expects parameter 2 to be array, null given
3
Комментарии

Сообщение об ошибке довольно описательное. Что именно вам непонятно? Пожалуйста, объясните. Функция in_array() требует, чтобы второй параметр был массивом, проверьте документацию PHP. Поэтому вам просто нужно убедиться, что второй параметр, который вы передаете в in_array(), является массивом. Это все, что касается сообщения об ошибке. Также обратите внимание, что этот вопрос, в текущем виде, касается общей ошибки PHP, что выходит за рамки этого сайта.

cybmeta cybmeta
19 окт. 2016 г. 12:50:28

@cybmeta после использования вашего кода из ответа и установки false в get_post_meta теперь второй параметр $item->content_multiple является массивом, но когда я выбираю несколько вариантов, сохраняется только последний вариант, а не все выбранные варианты

Shwan Namiq Shwan Namiq
19 окт. 2016 г. 15:13:05

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

cybmeta cybmeta
19 окт. 2016 г. 15:19:29
Все ответы на вопрос 2
14
22

Вы делаете так:

$item->content_multiple = get_post_meta($item->ID, '_menu_item_content_multiple', true);

Обратите внимание, что третий параметр get_post_meta() установлен в true, что означает возврат только одного значения. Если прочитать документацию, вы увидите, что при значении третьего параметра false вы получите массив со всеми значениями для этого мета-поля.

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

В вашем коде, похоже, все значения из множественного выбора сохраняются в одном мета-поле. Таким образом, вы получаете массив из запроса и передаёте его как единое значение мета-поля. Когда значение мета-поля является массивом, WordPress преобразует его в сериализованную строку перед сохранением в базе данных; это делается внутренне с помощью функции maybe_serialize(). Затем, при попытке получить значение мета-поля, если оно является сериализованной строкой, WordPress пропускает его через maybe_unserialize(), преобразуя строку обратно в массив:

Разберём на общем примере.

Вот массив значений, который будет сохранён в базе данных:

$values = [ 'red', 'yellow', 'blue', 'pink' ];

Если ваше мета-поле называется "color", у вас есть два варианта:

Несколько записей для одного мета-ключа

$values = [ 'red', 'yellow', 'blue', 'pink' ];
foreach( $values as $value ) {
    // Этот метод использует `add_post_meta()` вместо `update_post_meta()`
    add_post_meta( $item_id, 'color', $value );
}

Теперь элемент с идентификатором $item_id будет иметь несколько записей для мета-ключа color. Затем вы можете использовать get_post_meta() с третьим параметром false и получите массив со всеми значениями:

// Вам не обязательно явно указывать третий параметр,
// так как по умолчанию он равен false
$colors = get_post_meta( $item_id, 'color' );

// $colors должен быть массивом со всеми значениями мета-ключа color
var_dump( $colors );

Сохранение массива в одной записи мета-поля

В этом случае массив значений сериализуется перед сохранением в базе данных:

$values = [ 'red', 'yellow', 'blue', 'pink' ];
// WordPress делает это автоматически, если передаётся массив
// $values = maybe_serialize( $values );
update_post_meta( $item_id, 'color', $values );

Теперь элемент с идентификатором $item_id будет иметь только одну запись для мета-ключа color; значение будет сериализованной строкой, представляющей исходный массив. Затем вы можете использовать get_post_meta() с третьим параметром true (поскольку у вас только одна запись), и WordPress автоматически преобразует сериализованную строку обратно в массив:

$colors = get_post_meta( $item_id, 'color', true );
// WordPress делает это автоматически, если значение - сериализованный массив
// $colors = maybe_unserialize( $colors );

// $colors должен быть массивом
var_dump( $colors );

Вы можете применить тот же подход к множественному выбору в вашей форме.

С одной записью для мета-ключа:

if( ! empty( $_REQUEST['menu-item-content-multiple'][$menu_item_db_id] ) ) {
    $meta_field_value = $_REQUEST['menu-item-content-multiple'][$menu_item_db_id];
    // $meta_field_value будет автоматически сериализован WordPress
    update_post_meta( $menu_item_db_id, '_menu_item_content_multiple', $meta_field_value );
}

Затем вы можете использовать значение как массив:

 // Значение, возвращаемое get_post_meta(), автоматически десериализуется WordPress
 $item->content_multiple = get_post_meta( $item->ID, '_menu_item_content_multiple', true );

С несколькими записями:

Концепция здесь немного другая; у вас будет несколько записей в базе данных с одним мета-ключом, поэтому вам нужно использовать add_post_meta() вместо update_post_meta(), чтобы добавить новую запись для каждого значения.

if( ! empty( $_REQUEST['menu-item-content-multiple'][$menu_item_db_id] ) ) {
    $values = $_REQUEST[ 'menu-item-content-multiple' . $menu_item_db_id ];
    foreach( $values as $value ) {
        add_post_meta( $menu_item_db_id, '_menu_item_content_multiple', $value );
    }
}

Теперь вы можете использовать get_post_meta() с третьим параметром false (это значение по умолчанию, поэтому его можно опустить):

$item->content_multiple = get_post_meta( $item->ID, '_menu_item_content_multiple', false );

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

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

array_map( 'sanitize_text_field', wp_unslash( $_REQUEST['menu-item-content-multiple'][$menu_item_db_id] ) );
19 окт. 2016 г. 13:01:35
Комментарии

Спасибо, должен ли я использовать false вместо true. Но вы также использовали true в get_post_meta !!!

Shwan Namiq Shwan Namiq
19 окт. 2016 г. 13:28:37

В текущем виде вашего кода - нет, вы должны использовать методы serialize и unserialize. Чтобы использовать false в get_post_meta(), вам следует сохранять каждое значение из вашего select как отдельную запись в базе данных, с одним и тем же ключом, но многократно - то есть использовать update_post_meta() для каждого значения, но всегда с одним и тем же ключом; тогда вы сможете использовать false в get_post_meta() чтобы получить их все. Надеюсь, вы понимаете, что я имею в виду.

cybmeta cybmeta
19 окт. 2016 г. 13:50:05

Я обновил ответ, добавив, надеюсь, более понятное объяснение.

cybmeta cybmeta
19 окт. 2016 г. 13:59:53

Спасибо, ваш пост помог решить проблему с сообщением об ошибке. Я отредактировал свой пост, добавив поле выбора (select box) как в моем файле. Однако проблема в том, что при выборе нескольких вариантов после сохранения настроек сохраняется только последний выбранный вариант, а не все. Почему так происходит? Есть ли ошибка в использовании функции WordPress selected()? <?php echo selected(in_array($key, $item->content_multiple)); ?> (я использовал ваш второй вариант кода)

Shwan Namiq Shwan Namiq
19 окт. 2016 г. 14:57:49

Обратите внимание, что для использования нескольких записей с одним и тем же мета-ключом вам нужно выполнить update_post_meta() для каждого значения, но используя один и тот же ключ каждый раз, как я показал в ответе.

cybmeta cybmeta
19 окт. 2016 г. 15:21:03

Да, я хочу несколько записей с одним и тем же мета-ключом, но я не знаю, как использовать этот метод. Пожалуйста, помогите мне и объясните это с помощью кода.

Shwan Namiq Shwan Namiq
19 окт. 2016 г. 15:30:35

Давайте продолжим обсуждение в чате.

Shwan Namiq Shwan Namiq
19 окт. 2016 г. 15:43:38

Извините cybmeta, но у меня есть ещё одна проблема со снятием выбранных опций. Когда я пытаюсь снять выбор с выбранной опции, после сохранения настроек она остаётся выбранной, и я не могу снять выбор с любых опций после их выбора

Shwan Namiq Shwan Namiq
20 окт. 2016 г. 02:00:14

Просто используйте delete_post_meta() когда вам это нужно. Добавил базовый пример.

cybmeta cybmeta
20 окт. 2016 г. 10:39:08

Спасибо, это работает, но я не могу удалить все выбранные варианты, а также остается первый выбранный вариант. Функция delete_post_meta не удаляет первый выбранный вариант.

Shwan Namiq Shwan Namiq
20 окт. 2016 г. 14:06:26

WordPress автоматически сериализует/десериализует данные массива в одном поле метаданных (так же, как и функции опций), вам не следует делать это вручную.

Milo Milo
20 окт. 2016 г. 18:10:16

@Milo почему в этом ответе функция delete_post_meta не снимает выделение или не удаляет первый выбранный вариант.

Shwan Namiq Shwan Namiq
20 окт. 2016 г. 22:29:21

@Milo, хорошее замечание, я запутался в объяснениях в кодексе и на портале для разработчиков, но, посмотрев исходный код, могу подтвердить, что WordPress автоматически выполняет сериализацию/десериализацию за нас; ответ обновлен.

cybmeta cybmeta
21 окт. 2016 г. 09:59:41

@ShwanNamiq Думаю, будет лучше, если вы создадите новый вопрос для вашей новой проблемы.

cybmeta cybmeta
21 окт. 2016 г. 10:10:47
Показать остальные 9 комментариев
6

Вы используете функцию selected() неправильно. Функция selected() требует 1 обязательный параметр и 2 необязательных. В вашем случае лучше использовать $selected и $current.

Вы пропустили подчеркивание перед именем мета-поля в имени select.

Для selected():

selected( $selected, $current, $echo);

Вы можете прочитать больше об этом в codex справочник функций selected()

Ваш select должен выглядеть так:

<select name="_menu-item-content-multiple[]" multiple>
     <?php 
        foreach ($select_options as $key => $value) {
            echo '<option value="'.$key.'" '.selected(in_array($key, $item->content_multiple), $key).'>'.$value.'</option>';
        }
     ?>
</select>
19 окт. 2016 г. 12:12:38
Комментарии

Я использовал ваш код, но ошибка все равно появляется. Думаю, проблема в $item->content_multiple - оно должно сохраняться как массив, потому что $item->content_multiple сохраняет только одно значение и не может сохранить несколько значений как массив

Shwan Namiq Shwan Namiq
19 окт. 2016 г. 12:20:18

Хорошо, проверьте что имя вашего мета-поля всегда одинаковое, сейчас это не так. Если вы хотите чтобы оно было с префиксом подчеркивания, всегда используйте это подчеркивание, и назовите ваш select с подчеркиванием или с тире. update_post_meta работает, но значение не сохраняется.

Benoti Benoti
19 окт. 2016 г. 12:26:45

Я добавил более 10 полей как в примере выше - все работают без проблем. Говорю что ошибка в $item->content_multiple потому что это поле сохраняет только одиночное значение и не может сохранить несколько значений одновременно, так как не обновляется как массив. Перед циклом foreach когда я добавляю $item->content_multiple = array(); - ошибка исчезает, но значения не сохраняются

Shwan Namiq Shwan Namiq
19 окт. 2016 г. 12:38:26

Не совсем уверен, но проверь тип $item, это объект или массив? Хорошо, содержимое в нем - это массив, но попробуй преобразовать его в массив для функции in_array, перед циклом foreach: (array)$item и в selected() используй его как массив, вот так: $item['content_multiple']

Benoti Benoti
19 окт. 2016 г. 12:50:11

$item - это объект, а не массив array()

Shwan Namiq Shwan Namiq
19 окт. 2016 г. 15:01:34

in_array() принимает только массив в качестве второго параметра. Тебе нужно преобразовать объект в массив вот так: (array)$item, но оставь третий аргумент get_post_meta как true (чтобы получить объект, который потом можно будет преобразовать)

Benoti Benoti
19 окт. 2016 г. 15:16:32
Показать остальные 1 комментариев