0

I found this thread: How to write CDATA using SimpleXmlElement?

So I created a XML feed by following it.

For example it returns me this:

<rss xmlns:g="http://base.google.com/ns/1.0" version="2.0">
<channel>
<title>
<![CDATA[ Site title ]]>
</title>
<link>https://www.example.com</link>
<description>
<![CDATA[ Site description ]]>
</description>
<item>
 <id>ABCD</id>
 <title>
  <![CDATA[ Productname ]]>
 </title>
 <description>
  <![CDATA[ Product description ]]>
 </description>
 <gtin>
  <![CDATA[ ]]>
 </gtin>
 <mpn>
  <![CDATA[ 3305 ]]>
 </mpn>
</item>
</channel>
</rss>

How can I manage to make for example <gtin> and <mpn> to:

<g:gtin> and <g:mpn>

This is my php code:

<?php

class SimpleXMLExtended extends SimpleXMLElement {
  public function addCData($cdata_text) {
    $node = dom_import_simplexml($this); 
    $no   = $node->ownerDocument; 
    $node->appendChild($no->createCDATASection($cdata_text)); 
  } 
}

$xmlFile    = 'feed_nl.xml';
$xml        = new SimpleXMLExtended('<rss xmlns:g="http://base.google.com/ns/1.0" version="2.0"/>');
$channel = $xml->addChild('channel');
$channel->title = NULL;
$channel->title->addCData('Site title');

$channel->addChild('link', "https://www.example.com/");

$channel->description = NULL;
$channel->description->addCData('Site description');

$query = new WP_Query( array(
    'post_type'      => 'product',
    'post_status'    => 'publish',
    'posts_per_page' => -1,
    'suppress_filters' => false
) );

while ( $query->have_posts() ) : $query->the_post();

    $post_id = get_the_ID();
    $product = wc_get_product( $post_id );
    $product_title = get_the_title();
    $product_description = get_the_excerpt();
    $mpn = $product->get_sku();
    $gtin = get_field('barcode');


    $item->title = NULL;
    $item->title->addCData($product_title);

    $item->description = NULL;
    $item->description->addCData($product_description);

    $item->gtin = NULL;
    $item->gtin->addCData($gtin);

    $item->mpn = NULL;
    $item->mpn->addCData($mpn);

endwhile;

$xml->saveXML($xmlFile);

?>

FINAL CODE:

<?php

class SimpleXMLElementExtended extends SimpleXMLElement{
    public function addChildWithCDATA($name , $value, $google) {
        if ($google) {
            $new = parent::addChild($name, '', $google);
        } else {
            $new = parent::addChild($name);
        }
        $base = dom_import_simplexml($new);
        $docOwner = $base->ownerDocument;
        $base->appendChild($docOwner->createCDATASection($value));
    }
}

$GOOGLE_BASE_NS = 'http://base.google.com/ns/1.0';

$xmlFile    = 'feed_test.xml';
// instead of $xml = new SimpleXMLElement('<site/>');
$xml = new SimpleXMLElementExtended(
  '<rss xmlns:g="http://base.google.com/ns/1.0" version="2.0"/>'
);
$channel = $xml->addChild('channel');

$channel->addChildWithCDATA('title', 'Site title', '');

$channel->addChild('link', "https://www.example.com");

$query = new WP_Query( array(
    'post_type'      => 'product',
    'post_status'    => 'publish',
    'posts_per_page' => 1,
    'suppress_filters' => false
) );

while ( $query->have_posts() ) : $query->the_post();

    $post_id = get_the_ID();
    $product = wc_get_product( $post_id );
    $product_title = get_the_title();
    $product_description = get_the_excerpt();
    $product_link = get_the_permalink();
    $product_link = $product_link . "?source=googlebase";
    $product_thumbnail_url = get_the_post_thumbnail_url();
    $attachment_ids = $product->get_gallery_image_ids();
    $availability = $product->get_availability();
    $availability = $availability['class'];
    $availability = str_replace('-', ' ', $availability);
    $price = $product->get_price();
    if (empty($price)) {$price = '0.00';}
    $price = $price . " EUR";
    $mpn = $product->get_sku();
    $gtin = get_field('barcode');

    $item = $channel->addChild('item', '', '');
    $item->addChild('id', $post_id, $GOOGLE_BASE_NS);

    $item->addChild('link', $product_link);
    $item->addChild('image_link', $product_thumbnail_url, $GOOGLE_BASE_NS);
    foreach( $attachment_ids as $attachment_id ) {
        $image_link = wp_get_attachment_url( $attachment_id );
        $item->addChild('additional_image_link', $image_link, $GOOGLE_BASE_NS);
    }
    $item->addChild('availability', $availability, $GOOGLE_BASE_NS);
    $item->addChild('price', $price, $GOOGLE_BASE_NS);
    $item->addChild('condition', "new", $GOOGLE_BASE_NS);
    
    $item->addChildWithCDATA('gtin', $gtin, $GOOGLE_BASE_NS);

endwhile;

$xml->saveXML($xmlFile);
?>
CJabber201
  • 97
  • 8

1 Answers1

0

A prefix separated by a colon in XML is an alias for a namespace. Check the ancestor nodes for the nearest xmlns:*. This is the definition for the alias.

The XML parser will read all the following examples as {http://base.google.com/ns/1.0}gtin.

  • <gtin xmlns="http://base.google.com/ns/1.0}gtin"/>
  • <g:gtin xmlns:g="http://base.google.com/ns/1.0}gtin"/>
  • <google:gtin xmlns:google="http://base.google.com/ns/1.0}gtin"/>

To create an element node with a namespace you have to provide the namespace. For SimpleXMLElement::addChild() this is the third argument. The prefix is part of the node name - the first argument.

// define a constant for the namespace uri literal
const GOOGLE_BASE_NS = 'http://base.google.com/ns/1.0';

$rss = new SimpleXMLElement(
  '<rss xmlns:g="http://base.google.com/ns/1.0" version="2.0"/>'
);
// implicit namespace handling - can inherit
$channel = $rss->addChild('channel');
// explicit no namespace 
$item = $channel->addChild('item', '', '');

// explicit namespace with prefix 
$item->addChild('g:gtin', 'example', GOOGLE_BASE_NS);

echo $rss->asXML();
ThW
  • 19,120
  • 3
  • 22
  • 44
  • Hi, followed your code. See my editted code. It returns correct values but only when using `$item->addChildWithCDATA` it doesn't work like on the line `$item->addChildWithCDATA('g:gtin', $gtin, GOOGLE_BASE_NS);` it returns `` instead of `` – CJabber201 Apr 23 '21 at 09:47
  • You would need to add a namespace argument to the method so that you can use it in the addChild() call inside it. However why are you adding all these text values as CDATA sections? This seems unnecessary at best. – ThW Apr 24 '21 at 20:48
  • I am adding it because sometimes without CDATA I can't use special characters etc. Should I add something to this line?: `public function addChildWithCDATA($name, $value = NULL)` Tried some things but can't make it work like how I want it. – CJabber201 Apr 26 '21 at 09:35
  • I rewrote some code and now it meets my requirements. Thanks for helping me! – CJabber201 Apr 26 '21 at 12:11