views:

141

answers:

2

Hello all,

<epp>
    <domain:create xmlns:domain="urn:someurn">
       <domain:name></domain:name>
       <domain:registrant></domain:registrant>
       <domain:contact></domain:contact>
    </domain:create>
</epp>

I would like to add a child, on a very specific place (so I'm also using DOM and not only simpleXML) for <domain:create> node.

I have tried to use the $ns attribute on simpleXML construct.

$nsNode = new SimpleXMLElement('<domain:ns>', $options = 0, $ns='urn:ietf:params:xml:ns:domain-1.0');

//transform the target into dom object for manipulation
$nodeRegistrantDom = dom_import_simplexml($nodeRegistrant);

But I'm getting:

I/O warning : failed to load external entity "<domain:ns>"

I've tried to register the prefix after creating the element, but I use no xpath after this, so this was quite a useless try...

//creates the simpleXML object node to be inserted.
$nsNode = new SimpleXMLElement('<ns/>');

//this will not work, because we will not use xpath after it :s
$nsNode->registerXPathNamespace('domain', 'urn:ietf:params:xml:ns:domain-1.0');

Since the xml is loaded from a file, and that file as this ns declared, maybe we should grab it from that file?

Here is an overall of the above, so that we can better understand the context: We are loading a XML file that contains an overall structure:

 $xmlObj = simplexml_load_file('EppCreateDomain.xml');

They we will grab an element that we will use as a target:

//grab the target.
    $nodeRegistrant = $xmlObj->command->create->children(self::OBJ_URI_DOMAIN)->create->registrant;

    //transform the target into a dom object for later manipulation
    $nodeRegistrantDom = dom_import_simplexml($nodeRegistrant);

//we try to use simpleXML to create the node that we want to add after our target.
    $nsNode = new SimpleXMLElement('<domain:ns>');


//grabs the node and all his children (none in this case), by importing the node we want to add,
//into the root object element that contains the <domain:registrant> node.
$nsNodeDom = $nodeRegistrantDom->ownerDocument->importNode(dom_import_simplexml($nsNode), true);

$nodeRegistrantDom->parentNode->insertBefore($nsNodeDom, $nodeRegistrantDom->nextSibling);

$simpleXmlNsNode = simplexml_import_dom($nsNodeDom);

Now we have our node placed on a proper place. And converted to simpleXML so, we can now easily add some children and fill the rest of the xml file..

$hostAttr = $simpleXmlNsNode->addChild('domain:hostAttr');
$hostName = $hostAttr->addChild('domain:hostName');

Please advice, MEM

A: 

Since the xml is loaded from a file, and that file as this ns declared, maybe we should grab it from that file?

If that file is a XML file, yes, you should load the whole the file, not just a portion.

Once the namespace is declared, adding a namespaced element is easy:

<?php
$xml = <<<XML
<epp>
    <domain:create xmlns:domain="urn:someurn" xmlns:ietf="urn:thaturn">
       <domain:name></domain:name>
       <domain:registrant></domain:registrant>
       <domain:contact></domain:contact>
    </domain:create>
</epp>
XML;

$sxml = new SimpleXMLElement($xml);
$sxml->children("domain", true)->create->addChild("newElem", "value", "urn:thaturn");
echo $sxml->saveXML();

gives

<?xml version="1.0"?>
<epp>
    <domain:create xmlns:domain="urn:someurn" xmlns:ietf="urn:thaturn">
       <domain:name/>
       <domain:registrant/>
       <domain:contact/>
    <ietf:newElem>value</ietf:newElem></domain:create>
</epp>
Artefacto
Yes. I'm loading the XML from a file.Using addChild is a nice feature, however, I cannot use it on this case, because, it will place our added node *at the end* of all siblings, however, this one needs to be placed after <domain:registrant/> node. Any other way to archive this but, without relying on "addChild" ?K. Regards,MEM
MEM
@MEM See this question: http://stackoverflow.com/questions/3432358/using-addchild-with-an-index-position/3432441#3432441 You only have to use DOMDocument::createElementNS instead of DOMDocument::createElement
Artefacto
@Artefacto: I expected this even to be a question of the same user ;-) Anyway, it's a _bit_ different. In this case it's not a positional index but `needs to be placed after <domain:registrant/> node`.@MEM: Question is: Is there always a domain:registrant element already present? If not is there a fall-back rule?
VolkerK
I can successfully place a node after <domain:registrant/>, but I can do so, without a namespace. Yes. <domain:registrant/> will always be present.Thanks a lot.
MEM
@MEM Well, it's a trivial modification. Instead of passing the index, determine the position of that element in the `$chnodes` array.
Artefacto
@Artefacto - array_filter, and count, wow. :) I must confess that I got confused. I can of course do the modification and it will work as you said, almost for sure, but the thing is, I need to understand and, despite all, I was unable to get it. One day I will be capable of reading the code, in a way that I can understand even the trivial changes. That day isn't here yet, sorry for not being able to follow it. :(
MEM
A: 
<?php
// test document, registrant as first/last element and somewhere in between
$xmlObj = new SimpleXMLElement('<epp>
  <domain:create xmlns:domain="urn:someurn">
    <domain:name></domain:name>
    <domain:registrant></domain:registrant>
    <domain:contact></domain:contact>
  </domain:create>
  <domain:create xmlns:domain="urn:someurn">
    <domain:name></domain:name>
    <domain:contact></domain:contact>
    <domain:registrant></domain:registrant>
  </domain:create>
  <domain:create xmlns:domain="urn:someurn">
    <domain:registrant></domain:registrant>
    <domain:name></domain:name>
    <domain:contact></domain:contact>
  </domain:create>
</epp>');

foreach( $xmlObj->children("urn:someurn")->create as $create ) {
  $registrant = $create->registrant;
  insertAfter($registrant, 'domain:ns', 'some text');
}
echo $xmlObj->asXML();

function insertAfter(SimpleXMLElement $prevSibling, $qname, $val) {
  $sd = dom_import_simplexml($prevSibling);
  $newNode = $sd->ownerDocument->createElement($qname, $val);
  $newNode = $sd->parentNode->insertBefore($newNode, $sd->nextSibling);
  return simplexml_import_dom($newNode);
}

prints

<?xml version="1.0"?>
<epp>
  <domain:create xmlns:domain="urn:someurn">
    <domain:name/>
    <domain:registrant/><domain:ns>some text</domain:ns>
    <domain:contact/>
  </domain:create>
  <domain:create xmlns:domain="urn:someurn">
    <domain:name/>
    <domain:contact/>
    <domain:registrant/><domain:ns>some text</domain:ns>
  </domain:create>
  <domain:create xmlns:domain="urn:someurn">
    <domain:registrant/><domain:ns>some text</domain:ns>
    <domain:name/>
    <domain:contact/>
  </domain:create>
</epp>
VolkerK
I'm studying it... :) I will need a couple of hours. Yes I'm that dummy.
MEM
Hello again, can you guys please reply to this question:1) Both of your answers, have been in a form of a function. Why?Yes, it's a very basic question, but I'm a very basic coder as well, since I have no more then 1 year exp into this. :sThanks again,MEM
MEM
Doubts 2: Why createElement('domain:ns'); works, and this importNode(dom_import_simplexml($nsNode), true); doesn't ?
MEM
Doubt 3: When we use createElement, that means that it will be create but placed anywhere? And that's because of that THAT we can use insertBefore to properly place it?
MEM
Doubt 4: If we need to add some childs to our element, and those childs need a namespace prefix as well, we have to: $hostAttr = $simpleXmlNsNode->addChild('domain:hostAttr', null, self::OBJ_URI_DOMAIN);Why to we need to redeclare our namespace. Shouldn't be there when we return simplexml_import_dom($newNode); ?
MEM
Boolean answers my suffice. I'm sorry, the code works, it's perfect, I like it, but I would also love to properly understand it. :)Thanks.
MEM
lot of questions ;-) 1) Easier to use, easier to copy, simpler demo code. 2) You've create a new, separate document with `new SimpleXMLElement(...)` while createElement works "within" the same document where the ns is already registered. 3) createElement() itself does place the element anywhere in the document, you have to do that "yourself" e.g. via appendChild(),insertBefeore()... 4) ???
VolkerK
1) ok. 3) I've read the php manual part for createElement, clear. :) 4) Np. It deserves a question all by itself. Now: 2) I see... but we have imported to the owner document, or, at least that was my intention. :s ?I'm specificity referring to this line: $nsNodeDom = $nodeRegistrantDom->ownerDocument->importNode(dom_import_simplexml($nsNode), true);
MEM