Add, update and edit an XML file with PHP

2019-01-18 07:42发布

问题:

I have an xml file which I would like to create a form/table around to add, edit and delete records using PHP. Currently I use simpleXML to load the XML file, and display its content on various pages.

Is there any way I can create a table that shows all results, and allows me to either edit or delete that particular row of the table which represents a full record within the XML file.

When clicking edit I would like the details from the record to appear in a form which the user can change and then save out, updating the XML file and the corresponding web page.

I need this done in PHP, preferably using SimpleXML, though open to suggestions of other ways to do this with PHP.

Cheers

回答1:

XSLT is your friend for converting the XML database file to the format you want to display on the web-page. You create an XSL template that includes all the HTML you want for each record and then iterate through the XML file with a for-each statement. I'll give a rough overview and can help with more details if needed.

Here's the generic PHP file I use to do XSLT (process an XML file with an XSL file) via AJAX. This one is setup to work with required (and optional, if desired) inputs passed in a GET call from the browser. p#n and p#v (see comments at top of code below) are parameter and value pairs to be passed into the XSL document in cases where you want to use some input parameter to affect the output. In this case output is echoed back to the browser. Here's the PHP to run the XML database through and XSLT transformation to create the HTML for your web display (table, or whatever you put in the XSL file template):

<?php
//REQUIRED INPUTS:
// - xml: path to xml document
// - xsl: path to xsl style sheet
// - pCount: number of parameters to be passed to xslt (send zero '0' if none)
//OPTIONAL INPUTS (must have as many as specified in pCount, increment '1' in
//names below up a number for each iteration):
// - p1n: name of first parameter
// - p1v: value of first parameter

//SET Paths 
$xmlPath = $_GET['xml'];
$xslPath = $_GET['xsl'];

// Load the XML source
$xml = new DOMDocument;
$xml->load($xmlPath);

$xsl = new DOMDocument;
$xsl->load($xslPath);

// Configure the transformer
$proc = new XSLTProcessor;
$proc->importStyleSheet($xsl); // attach the xsl rules

//Set Parameter(s), if present
$xslParamCount = $_GET['pCount']; //Check number of xsl parameters specified in GET
for ($i=1; $i<=$xslParamCount; $i++){
   $xslParamName = $_GET['p'.$i.'n'];
   $xslParamValue = $_GET['p'.$i.'v'];
   $proc->setParameter( '', $xslParamName, $xslParamValue);    //Set parameters for XSLTProcessor
}

// TRANSFORM
echo $proc->transformToXML($xml);

// SET Mime Type
$mime = "application/xhtml+xml";
$charset = "iso-8859-1";
header("Content-Type: $mime;charset=$charset");
?> 

Below is an example of an XSL file template taking an XML database document and converting it to HTML for insertion in a web page. Note the HTML span tags. All the xsl tags are processing instructions that determines what goes in and around the HTML tags in the template. In this case, we are filtering results and choosing appropriate data to display based on input parameters passed into the XSL file (see xsl:param items at near top):

<?xml version='1.0' encoding='UTF-8'?>
<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='1.0'>
 <xsl:param name='testId'/>
 <!--Input options for following param: localUse, notLocalUse, nottestId, or '' (all)-->
 <xsl:param name='matchType'/>
 <xsl:template match='/'>
   <xsl:choose>
      <xsl:when test="$matchType='localUse'">
         <xsl:for-each select="//test[@id=$testId and @localUse='yes']">
            <xsl:sort select="../@id"/>
            <div><xsl:if test='../@localPrefTestId=$testId'><xsl:attribute name='class'>preferredTest</xsl:attribute></xsl:if>
               <span class='productStockCode'>
                  <xsl:if test='../@localPrefTestId=$testId'>
                     <xsl:attribute name='title'>Preferred test for this product</xsl:attribute>
                  </xsl:if>
                  <xsl:if test='../@localPrefTestId!=$testId'>
                     <xsl:attribute name='title'>Alternate (not preferred) test for this product - see note to right</xsl:attribute>
                  </xsl:if>
                  <xsl:value-of select='../@id'/>
               </span>
               <span class='productStockName'>
                  <xsl:if test='../@localPrefTestId=$testId'>
                     <xsl:attribute name='title'>Preferred test for this product</xsl:attribute>
                  </xsl:if>
                  <xsl:if test='../@localPrefTestId!=$testId'>
                     <xsl:attribute name='title'>Alternate (not preferred) test for this product - see note to right</xsl:attribute>
                  </xsl:if>
                  <xsl:value-of select='../@name'/>
               </span>
               <span class='producttestNote'>
                  <xsl:value-of select='child::localPrefNote'/>
               </span>
               <span class='productDeleteButton'>
                  <input onClick='remProdLink(this)' title='Click to remove link to this product' type='image' src='button_tiny_X_grey.bmp'></input>
               </span>
            </div>
         </xsl:for-each>
      </xsl:when>
      <xsl:otherwise>
         <p>Server Error: GET must specify matchType parameter value of 'localUse', 'notLocalUse', 'nottestId', or '' (for all)</p>
         <p>matchType received: <xsl:value-of select='$matchType'/></p>
      </xsl:otherwise>
   </xsl:choose>
 </xsl:template>
 <!--Note the output method="html" below is required for this to insert correctly into a web page; without this it may nest improperly if any divs are empty-->
 <xsl:output method="html" omit-xml-declaration="yes"/>
</xsl:stylesheet>

Note that all the xsl tests are XPath expressions.

To delete a row you can have a button on that row that calls a JavaScript function with the "this" argument (see onClick='remProdLink(this)' in code above) to reference the row, and then grab the unique identifier of the row in JavaScript something like this:

function remProdLink(obj){
   //get unique id via Dom from passed in object reference
   //edit everything after "obj" below to get to the unique id in the record
   var testCode = obj.parentNode.parentNode.firstChild.nextSibling.innerHTML;
   //code to send AJAX POST to server with required information goes here
}

On the server end, your PHP receives the AJAX POST with the unique identifier, loads the XML database file into simpleXml, finds the node via XPath, and removes it, something like this:

<?php
   //Move url encoded post data into variables
   $testCode = $_POST['testCode']; //$testCode should be a unique id for the record

   //load xml file to edit
   $xml = simplexml_load_file('yourDatabase.xml');

   //find target node for removal with XPath
   $targets = $xml->xpath("//testCode[@id=$testCode]");

   //import simpleXml reference into Dom to do removal
   $dom2 = dom_import_simplexml($targets[0]);
   $dom2->parentNode->removeChild($dom2);

   //format xml to save indented tree (rather than one line) and save
   $dom = new DOMDocument('1.0');
   $dom->preserveWhiteSpace = false;
   $dom->formatOutput = true;
   $dom->loadXML($xml->asXML());
   $dom->save('yourDatabase.xml');
?>

As for editing an item, you can have another JavaScript function called similarly to the one for deleting as noted above, create a form under that item on the web page with a save changes button, and when that button is pressed call another JavaScript function to AJAX POST to the server, again similarly to deleting. Only this time your POST will need to include all the information that could have been edited in the record along with the record's unique ID. PHP file will find the appropriate record (same as for deleting), and then you can either edit parts of that record in PHP, or just remove it and the create and append the new version of the record.

I'm not sure how many details you need. Hopefully this gives you a good start. Please comment on my answer if you need more details on any part of it. Good luck!



回答2:

I would suggest that you use DomDocument, and DomXPath, rather than SimpleXml. However, in general, XML is not an optimum medium for storage of data - You should probably use a database for whatever it is you're doing. If you need to exchange the data easily, you could perhaps use SQLite, instead of the more common MySql.



回答3:

You can use a native XML database to facilitate creating, adding, updating and retrieving xml documents and nodes. I've used BerkeleyDBXML (now part of Oracle) in the past with success. There's is a PHP library available as well.