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

Вы делаете так:
$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] ) );

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

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

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

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

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

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

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

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

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

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

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

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

Вы используете функцию 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>

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

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

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

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