jpneey
jpneey

Reputation: 674

PHP XML generation, chain `appendChild()`

I'm generating an XML file via PHP and I'm doing it this way:

$dom  = new DOMDocument();
$root = $dom->createElement('Root');
...
// some node definitions here etc
$root->appendChild($product);
$root->appendChild($quantity);
$root->appendChild($measureUnit);
$root->appendChild($lineNumber);
...
$dom->appendChild($root);
$dom->save( '/some/dir/some-name.xml');

It all works well until I encountered some problem, when I get to the part that I needed to append lets say N child nodes. This meant that I would be calling the function appendChild() 'N' times too - and that resulted on a very long php script which is a little hard to maintain.

I know we can split the main script on smaller files for better maintenance but are there better ways to just 'chain' the 'appendChild' calls so it would save as a lot of written lines or is there a somewhat magic function such as 'appendChildren' available?

This is my first time using the DOMDocument() class, I hope someone can shed me some light.

Thank you

Upvotes: 0

Views: 246

Answers (1)

ThW
ThW

Reputation: 19482

You can nest the DOMDocument::createElement() into DOMNode::appendChild() calls and chain child nodes or text content assignments.

Since PHP 8.0 DOMNode::append() can be used to append multiple nodes and strings.

$document = new DOMDocument();
// nest createElement inside appendChild
$document->appendChild(
    // store node in variable
    $root = $document->createElement('root')
);

// chain textContent assignment to appendChild
$root
  ->appendChild($document->createElement('product'))
  ->textContent = 'Example';
  
// use append to add multiple nodes  
$root->append(
  $product = $document->createElement('measureUnit'),
  $quantity = $document->createElement('quantity'),  
);
$product->textContent = 'cm';
$quantity->textContent = '42';


$document->formatOutput= true;
echo $document->saveXML();

Output:

<?xml version="1.0"?>
<root>
  <product>Example</product>
  <measureUnit>cm</measureUnit>
  <quantity>42</quantity>
</root>

I am using an interface for reusable and maintainable parts, usually:

interface XMLAppendable {
    
    public function appendTo(DOMElement $parent): void;
    
}

class YourXMLPart implements XMLAppendable {
    
    private $_product;
    private $_unit;
    private $_quantity;
    
    public function __construct(string $product, string $unit, int $quantity) {
        $this->_product = $product;
        $this->_unit = $unit;
        $this->_quantity = $quantity;
    }
    
    public function appendTo(DOMElement $parent): void {
        $document = $parent->ownerDocument; 
        $parent
            ->appendChild($document->createElement('product'))
            ->textContent = $this->_product;
        $parent
            ->appendChild($document->createElement('measureUnit'))
            ->textContent = $this->_unit;
        $parent
            ->appendChild($document->createElement('quantity'))
            ->textContent = $this->_quantity;
    }
}


$document = new DOMDocument();
// nest createElement inside appendChild
$document->appendChild(
    // store node in variable
    $root = $document->createElement('root')
);
$part = new YourXMLPart('Example', 'cm', 42);
$part->appendTo($root);

$document->formatOutput= true;
echo $document->saveXML();

Upvotes: 1

Related Questions