Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Extended XML saving, editing, etc...
#1
Hello guys.
Lately I've been working on a project where I had to write, save, edit, etc multiple items in xml files and found that there was not much possibilities to do this in the base GetSimple so I made a few new functions to handle the data and thought to share it if anyone needs also to get your suggestions how it could be improved.

First "issue" was with GetSimple getXML() since its always setting node cdata as text when you add or edit an item in the xml all the other cdata (except the one you add/edit) becomes simple text and this raised some problems for me.

So heres my modified code for that:
Note: The $file variable is the file you want to save in (Same like in GetSimple. ex: GSDATAOTHERPATH.'myfile.xml').
The $nocdata variable by default is true (to act the same way as the original function) but later if editing a file or adding a new element then it must be false so all the other/existing data is written back as it was, without changing cdata into text.
PHP Code:
function custom_getXML($file$nocdata true) {
    
$xml = @file_get_contents($file);
    if (
$xml) {
        if (
$nocdata) {
            
$data simplexml_load_string($xml'SimpleXMLExtended'LIBXML_NOCDATA);
        } else {
            
$data simplexml_load_string($xml'SimpleXMLExtended');
        }
        return 
$data;
    }


Then I needed a function to save the xml based on the passed variables, an identifier for each item and with another node for sorting.

Here is the code i came up with:
Note: The $file variable is the file you want to save in (ex: /absolute/path/to/your/file.xml).
The $values variable must be an array containing all the elements you want to add as keys and their values (ex: array('title'=>'This is the title','content'=>'This is the content')).
The $slug variable I use to redirect the user to a page if the xml was saved successfully.
PHP Code:
function custom_save_xml($file,$values,$slug) {
    if (!
file_exists($file)) {
        
$xml = new SimpleXMLExtended('<items></items>');
        
$item $xml->addChild('item');
        
$item->addAttribute('id','1');
        foreach (
$values as $key => $value) {
            
$base $item->addChild($key);
            
$base->addCData($value);
        }
        
$item->addChild('sort','1');
    } else {
        
$max_id = array();
        
$max_sort = array();
        
$xml custom_getXML($file,false);
        foreach (
$xml->item as $item) {
            
$max_id[] = (int)$item['id'];
            
$max_sort[] = (int)$item->sort;
        }
        
$id max($max_id)+1;
        
$sort_id max($max_sort)+1;
        
$item $xml->addChild('item');
        
$item->addAttribute('id',$id);
        foreach (
$values as $key => $value) {
            
$base $item->addChild($key);
            
$base->addCData($value);
        }
        
$item->addChild('sort',$sort_id);
    }
    if (!
XMLsave($xml$file)) {
        
//redirect user to error page
    
} else {
        
//redirect user to $slug
    
}
    


Then I needed a function to edit/save the data.
Here is what i came up with:

Note: The $file variable same like in the function above.
The $values variable same like in the function above except that it needs an extra key/value, the identifier of the element you want to edit (ex: array('id'=>'1','title'=>'This is the title','content'=>'This is the content')).
The $keep variable is optional. If the variable is passed then it must be an array containing the elements of which you want to keep the old values (ex: array('content') -> this way all the other passed $values will be updated except the 'content').
The $slug variable same like in the function above.
PHP Code:
function custom_save_edit_xml($file,$values,$slug,$keep=null) {
    if (!
file_exists($file)) {
        
//redirect user to error page
    
} else {
        
$xml custom_getXML($file,false);
        foreach(
$xml->item as $item) {
            if(
$item['id'] == $values['id']) {
                
$old = array();
                
$old_data = array();
                if (!
is_null($keep) && is_array($keep)) {
                    foreach (
$keep as $stuff) {
                        
$old[] = $stuff;
                    }
                    if (
count($old)>0) {
                        foreach (
$old as $other) {
                            
$old_data[$other] = (string)$item->$stuff;
                        }
                    }
                }
                
$oldsort $item->sort;
                
$dom=dom_import_simplexml($item);
                
$dom->parentNode->removeChild($dom);
                
$newitem $xml->addChild('item');
                
$newitem->addAttribute('id',$values['id']);
                unset(
$values['id']);
                foreach (
$values as $key => $value) {
                    
$base $newitem->addChild($key);
                    
$base->addCData($value);
                }
                if (
count($old_data)>0) {
                    foreach (
$old_data as $id=>$data) {
                        
$extra $newitem->addChild($id);
                        
$extra->addCData($data);
                    }
                }
                
$newitem->addChild('sort',$oldsort);
            }
        }
    }
    if (!
XMLsave($xml$file)) {
        
//redirect user to error page
    
} else {
        
//redirect user to $slug
    
}
    


After this i needed a function to delete data.
Note: The $file variable same like in the function above.
The $values variable is different from the other functions. Here it must be an array containing only the identifier of the element you want to delete (ex: array('id'=>'1')).
The $slug variable same like in the function above.
PHP Code:
function custom_delete_xml($file,$values,$slug) {
    if (!
file_exists($file)) {
        
//redirect user to error page
    
} else {
        
$xml custom_getXML($file);
        foreach(
$xml->item as $item) {
            if(
$item['id'] == $values['id']) {
                
$dom=dom_import_simplexml($item);
                
$dom->parentNode->removeChild($dom);
            }
        }
    }
    if (!
XMLsave($xml$file)) {
        
//redirect user to error page
    
} else {
        
//redirect user to $slug
    
}


And then for editing the 'sort' I made different things.
First is to edit only the sort element of the data.
Note: The $file variable same like in the function above.
The $values variable is different from the other functions. Here it must be an array containing only the identifier of the element and the new sort value (ex: array('id'=>'1','sort'=>'8')).
The $slug variable same like in the function above.
PHP Code:
function custom_sort_xml($file,$values,$slug) {
    if (!
file_exists($file)) {
        
//redirect user to error page
    
} else {
        
$xml custom_getXML($file,false);
        foreach(
$xml->item as $item) {
            if(
$item['id'] == $values['id']) {
                
$oldentries = array();
                foreach (
$item as $key => $value) {
                    
$oldentries[$key] = (string)$value;
                }
                unset(
$oldentries['@attributes'],$oldentries['id'],$oldentries['sort']);
                
$dom=dom_import_simplexml($item);
                
$dom->parentNode->removeChild($dom);
                
$item $xml->addChild('item');
                
$item->addAttribute('id',$values['id']);
                foreach (
$oldentries as $key => $value) {
                    
$base $item->addChild($key);
                    
$base->addCData($value);
                }
                
$item->addChild('sort',$values['sort']);
            }
        }
    }
    if (!
XMLsave($xml$file)) {
        
//redirect user to error page
    
} else {
        
//redirect user to $slug
    
}


And here the last one that I use often when working with xml files:
Note: The $object variable must be an object.
PHP Code:
function custom_object_to_array($object) {
    
$array_object is_object($object) ? get_object_vars($object) : $object;
    
$array = array();
    foreach (
$array_object as $key => $val) {
        
$val = (is_array($val) || is_object($val)) ? custom_object_to_array($val) : $val;
        
$array[$key] = $val;
    }
    return 
$array;


So if you guys have any suggestions on how this code could be improved, extended or if you think its totally useless please let me know by leaving a post.
Reply
#2
Is there a reason you did not use xpath for all this ?

Also this brings up some interesting issues, I did not know cdata was not preserving.
This should be looked into and fixed in core

So if get and save it will strip cdata?
I wonder if this is on purpose to support all the old methods we have that still strip_decode
NEW: SA Admin Toolbar Plugin | View All My Plugins
- Shawn A aka Tablatronix
Reply
#3
Well there is no reason for not using xpath its just that first I totally forgot about it then I thought I might change it later. Thanks for reminding me about it, gonna work rewrite it later.

As for the cdata thing if you pass LIBXML_NOCDATA to simplexml_load_string then all the CData will be merged as text nodes so when saving all CData will be saved back as text.

Actually i didnt really messed yet to see where the strip_decode is used but yeah it can be a 'fallback' support for those too while being able to use cdata aswell.

Not sure if im clear so let me know it.
Reply




Users browsing this thread: 1 Guest(s)