質問

I need to create an endpoint that when called returns an XML with the products of the store. I have the XML generated, but I don't know how to make the endpoint return it in XML format, because now it returns it in JSON format, and being an XML, it does not return it well.

This is my code:

<?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_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',
   ]);

});

But when I make the call to the endpoint what it returns is this:

"<?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"

How can I make it return it in XML format? Thank you very much.

Regards.

役に立ちましたか?

解決

By default, the output returned by your endpoint/callback will always be sent as a JSON-encoded string (and the Content-Type header would also be/contain application/json), so you can't use that callback for sending an XML feed.

However, you can use the rest_pre_serve_request hook if you want your REST API route to serve a different type of content like the XML feed in your case.

So for example, you can do:

  1. Set your callback (smg_feed()) to return the XML data which will be echoed in the maybe_smg_feed() below: (note that the permission_callback should always be set, check the REST API handbook for more details)

    function smg_feed( $request ) {
        // ... your code ...
        return 'your XML data';
    }
    
    add_action( 'rest_api_init', function () {
        register_rest_route( 'smg/v1', 'feed', [
            'methods'             => 'GET',
            'callback'            => 'smg_feed', // make sure it returns an XML string
            'permission_callback' => '__return_true',
        ]);
    });
    
  2. Then send the XML response like so:

    function maybe_smg_feed( $served, $result, $request, $server ) {
        // Bail if the route of the current REST API request is not our custom route.
        if ( '/smg/v1/feed' !== $request->get_route() ||
            // Also check that the callback is smg_feed().
            'smg_feed' !== $request->get_attributes()['callback'] ) {
            return $served;
        }
    
        // Send headers.
        $server->send_header( 'Content-Type', 'text/xml' );
    
        // Echo the XML that's returned by smg_feed().
        echo $result->get_data();
    
        // And then exit.
        exit;
    }
    add_filter( 'rest_pre_serve_request', 'maybe_smg_feed', 10, 4 );
    

Alternatively (as I mentioned in the original answer), without using the REST API (which does work), you can use add_feed() to deliver the XML feed.

( PS: Thanks to @TimothyJacobs for his help/comment. :) )

ライセンス: CC-BY-SA帰属
所属していません wordpress.stackexchange
scroll top