Вопрос или проблема
У меня есть мета-ключ (географические координаты), зарегистрированный как объект с двумя свойствами для пользовательского типа постов — lat
и lng
в PHP следующим образом:
register_post_meta(
'event',
'coordinates',
[
'description' => __( 'Широта и Долгота', 'zzz' ),
'show_in_rest' => [
'schema' => [
'type' => 'object',
'properties' => [
'lat' => [
'type' => 'number',
'optional' => true,
],
'lng' => [
'type' => 'number',
'optional' => true,
],
],
],
],
'single' => true,
'type' => 'object',
]
);
У меня также есть блок Gutenberg, который сохраняет метаданные вводимых пользователем координат, как показано здесь:
...
const [ meta, setMeta ] = useEntityProp(
'postType',
'event',
'meta',
postId
);
...
onChange={ ( value ) =>
setMeta( {
...meta,
coordinates: {
lat: value,
lng: coordinates?.lng,
},
} )
}
При сохранении поста я вижу запрос POST
к API, отправляющий данные в виде объекта.
Проблема
Тем не менее, мета-пост сохраняется в базе данных как сериализованный массив!
a:2:{s:3:"lat";d:40.1586012;s:3:"lng";d:-83.0750746;}
Я, конечно, могу преобразовать массив в объект при извлечении значения мета, но есть ли явная причина, почему он не сохраняется как сериализованный объект изначально, и есть ли способ переопределить способ сохранения значения?
Тем не менее, мета-пост сохраняется в базе данных как сериализованный массив!
Это связано с тем, что все мета-значения являются строками, такова схема таблицы базы данных.
Поэтому, если вы попытаетесь передать структурированные данные, WordPress старается помочь и использует serialize
, чтобы преобразовать их в строку, а затем десериализует их при извлечении. Обратите внимание, что это также имеет последствия для безопасности!. Это происходит для опций, временных значений, мета-пользователей, мета-постов, мета-терминов и т. д. Где угодно, где принимаются произвольные структуры данных PHP, они сериализуются в строку, чтобы их все еще можно было сохранить. Это также касается структур данных JS и т. д., которые отправляются в REST API, а также любых структурированных данных, которые вы пытаетесь передать через блоки API, их нельзя хранить в виде массива JS или объекта PHP, их необходимо сериализовать в строку перед сохранением.
Это также имеет серьезные последствия для производительности и практичности, запрос значений гораздо труднее и иногда невозможен, так как вы не можете надежно запрашивать подсоставные части значения мета, а MySQL не осведомлен о внутренней структуре значения. Это также означает, что если вы хотите проводить сравнения по значениям широты и долготы, это будет невозможно.
и есть ли способ переопределить способ сохранения значения?
Вы можете объявить его как строку и выполнить сериализацию сами, JSON был бы превосходным методом, и вы будете явно указывать, как данные хранятся.
Но в принципе это всегда будет строкой. Однако можно утверждать, что использование объекта изначально является ошибкой!
Например, если вы вместо этого будете хранить свою широту и долготу в отдельных значениях, вы можете упростить свой код, избежать сериализации и получить возможность использовать оператор сравнения в meta_query
.
В общем, лучше избегать хранения структурированных данных в мета-постах, если вы не собираетесь сериализовать их сами и извлекать в полном объеме, например, фрагмент JSON или XML, или содержимое.
Ответ или решение
Сохранение мета-значения в виде объекта с использованием useEntityProp
Введение
При разработке на платформе WordPress, особенно в контексте создания пользовательских типов записей и блоков Gutenberg, часто возникает необходимость работать с мета-данными. Одной из таких задач является сохранение географических координат (широты и долготы) в виде объекта. В этом материале мы рассмотрим процесс реализации такой функциональности и разберемся, почему сохранение мета-значения происходит в виде сериализованного массива, а не объекта.
Регистрация мета-значения
Для начала, необходимо корректно зарегистрировать мета-значение, которое будет хранить информацию о координатах. Пример кода на PHP для регистрации мета-ключа выглядит следующим образом:
register_post_meta(
'event',
'coordinates',
[
'description' => __( 'Latitude and Longitude', 'zzz' ),
'show_in_rest' => [
'schema' => [
'type' => 'object',
'properties' => [
'lat' => [
'type' => 'number',
'optional' => true,
],
'lng' => [
'type' => 'number',
'optional' => true,
],
],
],
],
'single' => true,
'type' => 'object',
]
);
В этом коде мы объявляем мета-значение coordinates
для типа записи event
, задавая его как объект с двумя параметрами: lat
и lng
.
Использование useEntityProp в Gutenberg
На клиентской стороне, для сохранения данных в Gutenberg-блоке, мы используем хук useEntityProp
. Код, который сохраняет координаты, может выглядеть так:
const [meta, setMeta] = useEntityProp(
'postType',
'event',
'meta',
postId
);
onChange={ (value) => {
setMeta({
...meta,
coordinates: {
lat: value,
lng: coordinates?.lng,
},
});
}}
Этот код эффективно обновляет мета-значение coordinates
с новыми значениями широты и долготы.
Проблема сериализации
Проблема, с которой вы столкнулись, заключается в том, что после сохранения мета-значения в базе данных оно хранится в виде сериализованного массива, а не объекта. Выглядит это как:
a:2:{s:3:"lat";d:40.1586012;s:3:"lng";d:-83.0750746;}
Причина этого поведения заключается в том, что все мета-значения в WordPress хранятся в виде строк. Это означает, что при передаче сложных структур, таких как объекты, WordPress сериализует их в строку перед сохранением в базе данных. При извлечении данные снова десериализуются, чтобы вернуть их в исходный вид.
Решения и рекомендации
Хотя это поведение является частью архитектуры WordPress, вы можете оптимизировать процесс сохранения данных. Вот несколько рекомендаций:
-
Хранение значений отдельно: Вместо сохранения координат в одном объекте, рассмотрите возможность хранения широты и долготы как отдельных мета-значений. Это не только упрощает код, но и позволяет использовать операторы сравнения в мета-запросах.
-
Использование JSON для сериализации: Если вам нужно сохранить данные как объект, рассмотрите возможность использования JSON для сериализации. Сохраняйте данные в формате JSON-строки и десериализуйте их по мере необходимости.
Пример кода для изменения сериализации:
register_post_meta(
'event',
'coordinates',
[
'description' => __( 'Latitude and Longitude', 'zzz' ),
'show_in_rest' => true,
'single' => true,
'type' => 'string',
]
);
Заключение
Таким образом, при работе с мета-значениями в WordPress необходимо учитывать особенности хранения данных. Хранение сложных структур, таких как объекты, может привести к трудностям с сериализацией. Мы настоятельно рекомендуем использовать более простые и чистые подходы к проектированию мета-данных, чтобы избежать таких проблем в будущем. Следуя этим рекомендациям, вы сможете оптимизировать работу с метаданными и улучшить взаимодействие с базой данных в ваших проектах на WordPress.