xmlforeachattributesmarkersprocesswire

Check if Child is already in XML File


I'm a bit stuck here. For the purpose of a Google map with multiple icons for different places, I need the data in a XML Format. The idea is to check whether an entry (identified by an ID out of Processwire) is already in the XML File or not. If it is, nothing has to be done. If it is not, it has to be added to the File. Also, if the id is on the XML File but not on the database anymore (because it had to be deleted), it needs to be removed.

This is what I have so far:

$file = 'xml/markers.xml';
$xml = simplexml_load_file($file);

foreach ($pages->get('/produzenten/')->prod_repeater as $produzent) {               

$marker = $xml->addChild("marker");

$marker->addAttribute('id', $produzent->id);
$marker->addAttribute('name', $produzent->prod_name);
$marker->addAttribute('address', $produzent->prod_objekt . ', 8625 Gossau'  );               
$marker->addAttribute('type', $produzent->prod_kategorie);                
$marker->addAttribute('lat', $produzent->geo_lg);                
$marker->addAttribute('lng', $produzent->geo_bg);               

file_put_contents('xml/markers.xml', $xml->asXML());        
}

This works, but adds the whole bunch of entries to the file everytime the website is called, which is not what I want.

Any help out there?

Here's the XML-File we're talking about: Markers


Solution

  • You can simply access xml elements with the same syntax you would access arrays or objects:

    $xml->someList[3]["someAttribute"];
    

    And then of course you can compare values and decide what to do.

    I changed your code a bit for my testing, but I guess you will get the idea:
    (Also this might not be the most elegant solution. idk)

    <?php
    $file = './markers.xml';
    $xml = simplexml_load_file($file);
    
    $prods = [
        [1, 'mark'],
        [2, 'anne'],
        [3, 'bernd'],
        [4, 'rose']
    ];
    
    foreach ($prods as $prod) {               
    
        $prodExists = false;
        foreach ($xml as $xmlMarker) {
            if($xmlMarker['id'] == $prod[0]) $prodExists = true;
        }
        if($prodExists) continue;
    
        $marker = $xml->addChild('marker');
        $marker->addAttribute('id', $prod[0]);
        $marker->addAttribute('name', $prod[1]);
    
    }
    
    file_put_contents('./markers.xml', $xml->asXML());
    

    You also probably shouldn't to file_put_contents in the loop, you will write to disk in every single iteration of the loop. Instead just write the file out once after the loop.
    As soon as you ->addChild() a new child is added to the xml tree in memory and you can just virtually pile them up until you finally write the file out.

    edit:

    Oh ... of course ... you basically just want to put all $prods from your ProcessWire data into a fresh XML file (keeping existing prods, add new, removing non existing => just put all the prods in a new file)

    <?php
    
    $newXml = new SimpleXMLElement('<markers></markers>');
    
    $prods = $pages->get('/produzenten/')->prod_repeater;
    
    foreach($prods as $prod) {
        $marker = $xml->addChild("marker");
        $marker->addAttribute([...]); // all your attributes
    }
    
    file_put_contents('xml/markers.xml', $newXml->asXML());
    

    edit2:

    ... even better:

    Create a new template (e.g. produzenten.xml.php) and a page (e.g. produzenten.xml) which will output the xml file. So the url would be something like this: www.domain.ch/produzenten/produzenten.xml

    This can also be cached and you can set a rule to delete the cache if the "Produzenten" page changes.

    The template file could be just this:

    <?php
    
    header('Content-type: application/xml');
    echo '<?xml version="1.0" standalone="yes"?><markers>';
    
    foreach($pages->get('/produzenten/')->prod_repeater as $prod) {
        echo '<marker id="' . $prod->id . '" name="' . $prod->name . '"/>';
    }
    echo '</markers>';