Вопрос или проблема
Мне нужно создать конечную точку, которая при вызове возвращает XML с продуктами магазина. У меня есть сгенерированный XML, но я не знаю, как сделать так, чтобы конечная точка возвращала его в формате XML, потому что сейчас она возвращает его в формате JSON, и как XML он не отображается правильно.
Вот мой код:
<?php
/**
* Название плагина: пример
* URI плагина: https://www.example.es/
* Описание: пример
* Версия: 1.0
* Автор: пример
* URI автора: https://www.example.es/
*/
function smg_feed() {
$args = array( 'post_status' => 'publish', numberposts => -1 );
$products = wc_get_products($args);
$xml_header="<?xml version=\"1.0\" encoding=\"UTF-8\"?><Document></Document>";
$xml = new SimpleXMLElement($xml_header);
foreach($products as $product) {
$data = $product->get_data();
$sku = $data['sku'];
$categoriasNombres = array();
$subcategoriasNombres = array();
foreach( wp_get_post_terms( $data['id'], 'product_cat' ) as $term ){
if( $term ){
if ($term->name == 'XXXX' || $term->name == 'YYYY') {
array_push($categoriasNombres, $term->name);
} else {
array_push($subcategoriasNombres, $term->name);
}
}
}
$categoriasNombres = implode(',', $categoriasNombres);
$subcategoriasNombres = implode(',', $subcategoriasNombres);
$propiedadesNombres = array();
foreach( wp_get_post_terms( $data['id'], 'product_tag' ) as $term ){
if( $term ){
array_push($propiedadesNombres, $term->name);
}
}
$propiedadesNombres = implode(',', $propiedadesNombres);
$nombre = $data['name'];
$formatosNombres = array();
foreach( wp_get_post_terms( $data['id'], 'pa_formato' ) as $term ){
if( $term ){
array_push($formatosNombres, $term->name);
}
}
$formatosNombres = implode(',', $formatosNombres);
$metaData = $data['meta_data'];
foreach($metaData as $item) {
if ($item->key == '_role_based_price') {
$obj = $item->value;
$precio = $obj['api1']['regular_price'];
$precioEspecial = $obj['api2']['regular_price'];
}
}
$row = $xml->addChild('row');
$row->addChild('sku', $sku);
$row->addChild('categorias', $categoriasNombres);
$row->addChild('subcategorias', $subcategoriasNombres);
$row->addChild('propiedades', $propiedadesNombres);
$row->addChild('nombre', $nombre);
// $row->addChild('imagen', );
$row->addChild('formatos', $formatosNombres);
$row->addChild('precio', $data['regular_price']);
$row->addChild('precio_especial', $precioEspecial);
}
$output = $xml->asXML();
return $output;
}
add_action('rest_api_init', function() {
register_rest_route('smg/v1', 'feed', [
'methods' => 'GET',
'callback' => 'smg_feed',
]);
});
Но когда я вызываю конечную точку, она возвращает следующее:
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n
<Document>
<row>
<sku\/>
<categorias>Nutrici\u00f3n<\/categorias>
<subcategorias>Nutridefense Plus<\/subcategorias>
<propiedades>todo<\/propiedades>
<nombre>NutriDefense plus<\/nombre>
<formatos\/>
<precio>29,90<\/precio>
<precio_especial>26,52<\/precio_especial>
<\/row>
<\/Document>\n"
Как мне сделать так, чтобы он возвращал это в формате XML? Большое спасибо.
С уважением.
По умолчанию вывод, возвращаемый вашей конечной точкой/обработчиком, всегда будет отправляться как строка, закодированная в JSON (а заголовок Content-Type
также будет/содержит application/json
), так что вы не можете использовать этот обработчик для отправки XML-канала.
Тем не менее, вы можете использовать rest_pre_serve_request
хук, если хотите, чтобы ваш маршрут REST API обслуживал другой тип контента, как, например, XML-канал в вашем случае.
Так что, например, вы можете сделать следующее:
-
Настройте ваш обработчик (
smg_feed()
) так, чтобы он возвращал XML-данные, которые будут выведены вmaybe_smg_feed()
ниже: (обратите внимание, чтоpermission_callback
всегда должен быть установлен, смотрите руководство по REST API для получения более подробной информации)function smg_feed( $request ) { // ... ваш код ... return 'ваши XML данные'; } add_action( 'rest_api_init', function () { register_rest_route( 'smg/v1', 'feed', [ 'methods' => 'GET', 'callback' => 'smg_feed', // убедитесь, что он возвращает строку XML 'permission_callback' => '__return_true', ]); });
-
Затем отправьте XML-ответ следующим образом:
function maybe_smg_feed( $served, $result, $request, $server ) { // Прекратите выполнение, если маршрут текущего запроса REST API не наш собственный маршрут. if ( '/smg/v1/feed' !== $request->get_route() || // Также проверьте, что обработчик - smg_feed(). 'smg_feed' !== $request->get_attributes()['callback'] ) { return $served; } // Отправьте заголовки. $server->send_header( 'Content-Type', 'text/xml' ); // Выведите XML, который возвращает smg_feed(). echo $result->get_data(); // И затем завершите выполнение. exit; } add_filter( 'rest_pre_serve_request', 'maybe_smg_feed', 10, 4 );
В качестве альтернативы (как я уже упоминал в оригинальном ответе), не используя REST API (что работает), вы можете использовать add_feed()
, чтобы предоставить XML-канал.
( PS: Спасибо @TimothyJacobs за его помощь/комментарий. 🙂 )
function smg_feed( $request ) {
....
echo $xml->asXML();
return new WP_REST_Response( null, 200, ['Content-Type' => 'text/xml'] );
}
Ответ или решение
Чтобы создать конечную точку (endpoint), которая возвращает XML с продуктами магазина, вам нужно внести некоторые изменения в ваш текущий код. Поскольку стандартный обработчик REST API WordPress именно так обрабатывает ответы, что они возвращаются в формате JSON, нам нужно будет использовать хук rest_pre_serve_request
, чтобы отправить XML-ответ с правильным заголовком.
Вот шаги, как это сделать:
-
Изменить вашу функцию
smg_feed
, чтобы она возвращала XML как текст, а также использовать новый типWP_REST_Response
для правильной установки заголовка. -
Добавить фильтр
rest_pre_serve_request
, который будет отправлять XML в ответе на ваш запрос.
Вот ваш обновленный код:
<?php
/**
* Plugin Name: example
* Plugin URI: https://www.example.es/
* Description: example
* Version: 1.0
* Author: example
* Author URI: https://www.example.es/
*/
function smg_feed() {
// Извлекаем все опубликованные продукты
$args = array('post_status' => 'publish', 'numberposts' => -1);
$products = wc_get_products($args);
// Создаем XML-структуру
$xml_header = '<?xml version="1.0" encoding="UTF-8"?><Document></Document>';
$xml = new SimpleXMLElement($xml_header);
foreach ($products as $product) {
$data = $product->get_data();
$sku = $data['sku'];
$categoriesNames = [];
$subcategoriesNames = [];
// Получаем термины категорий
foreach (wp_get_post_terms($data['id'], 'product_cat') as $term) {
if ($term) {
if ($term->name == 'XXXX' || $term->name == 'YYYY') {
array_push($categoriesNames, $term->name);
} else {
array_push($subcategoriesNames, $term->name);
}
}
}
// Преобразуем массивы в строки
$categoriesNames = implode(',', $categoriesNames);
$subcategoriesNames = implode(',', $subcategoriesNames);
$propertiesNames = [];
// Получаем термины свойств
foreach (wp_get_post_terms($data['id'], 'product_tag') as $term) {
if ($term) {
array_push($propertiesNames, $term->name);
}
}
$propertiesNames = implode(',', $propertiesNames);
$name = $data['name'];
$formatsNames = [];
// Получаем термины форматов
foreach (wp_get_post_terms($data['id'], 'pa_formato') as $term) {
if ($term) {
array_push($formatsNames, $term->name);
}
}
$formatsNames = implode(',', $formatsNames);
$metaData = $data['meta_data'];
$specialPrice = '';
// Получаем цены
foreach ($metaData as $item) {
if ($item->key == '_role_based_price') {
$obj = $item->value;
$price = $obj['api1']['regular_price'];
$specialPrice = $obj['api2']['regular_price'];
}
}
// Добавляем данные продукта в XML
$row = $xml->addChild('row');
$row->addChild('sku', $sku);
$row->addChild('categories', $categoriesNames);
$row->addChild('subcategories', $subcategoriesNames);
$row->addChild('properties', $propertiesNames);
$row->addChild('name', $name);
$row->addChild('formats', $formatsNames);
$row->addChild('price', $price);
$row->addChild('special_price', $specialPrice);
}
// Возвращаем XML в виде строки
return $xml->asXML();
}
// Регистрация маршрута
add_action('rest_api_init', function() {
register_rest_route('smg/v1', 'feed', [
'methods' => 'GET',
'callback' => 'smg_feed',
'permission_callback' => '__return_true',
]);
});
// Хук для установки заголовков и возвращения ответа
add_filter('rest_pre_serve_request', function($served, $result, $request, $server) {
// Проверяем, вызываем ли мы нужный маршрут
if ('/smg/v1/feed' !== $request->get_route() || 'smg_feed' !== $request->get_attributes()['callback']) {
return $served;
}
// Устанавливаем заголовок ответа
$server->send_header('Content-Type', 'text/xml');
// Отправляем XML
echo $result->get_data();
// Завершаем выполнение
exit;
}, 10, 4);
?>
Объяснение изменений:
-
Функция
smg_feed()
: Мы возвращаем XML-строку, созданную с помощьюSimpleXMLElement
. Теперь мы не используемreturn
для ответа в формe JSON. -
Фильтр
rest_pre_serve_request
: Этот фильтр позволяет изменить способ обработки ответа и позволяет настроить, что будет отправлено в ответе. Мы устанавливаем заголовокContent-Type
наtext/xml
, и выводим содержимое XML напрямую, завшая выполнение скрипта с помощьюexit
.
Эти изменения позволят вашему API возвращать XML-ответ, как это нужно в вашем случае. Теперь, делая запрос к вашему конечному пути /wp-json/smg/v1/feed
, вы получите ответ в нужном формате XML.