Im working on a project that allows for the user to append strings to an XML file. I have the appending portion working well, but what I also need a way to prevent the user from adding duplicate entires into each groups of strings
for each tag
section. The same strings are ok to appear in other parts of the XML document. Below is my code that works with appedending the file and the xml file im editing. I tried to write some code in the if statment that deals with appending to the XML trying to compair if the posted value is in the the array of strings but I think that I might be a bit off with it. I'll put a comment where it is. Any help would be helpful. This code is part of a project that I've posted on here but is in no way a duplicate question.
<?php
error_reporting( E_ALL );
/*
We should ONLY proceed if these POST fields
are actually set.
*/
if( $_SERVER['REQUEST_METHOD']=='POST' && isset(
$_POST['location'],
$_POST['fileName'],
$_POST['symbol']
)){
/*
Prepare filters
*/
$args=array(
'symbol' => array('filter' => FILTER_SANITIZE_ENCODED, 'flags' => FILTER_REQUIRE_ARRAY ),
'location' => array('filter' => FILTER_SANITIZE_ENCODED, 'flags' => FILTER_REQUIRE_ARRAY ),
'fileName' => FILTER_SANITIZE_ENCODED
);
/*
modify the POST array based upon the filters and
extract the values directly into variables.
*/
$_POST=filter_input_array( INPUT_POST, $args );
extract( $_POST );
/*
set options for dealing with XML.
*/
libxml_use_internal_errors( true ) ;
$dom=new DOMDocument('1.0','UTF-8');
$dom->recover=true;
$dom->formatOutput=true;
$dom->preserveWhiteSpace=false;
$dom->validateOnParse=false;
$dom->strictErrorChecking=false;
$dom->load( urldecode( $fileName ) );
$xp=new DOMXPath( $dom );
/*
Iterate through all POSTed items
*/
foreach( $symbol as $index => $code ){
/* Locate the relevant section within the XML based upon the tag */
$loc=urldecode( $location[ $index ] );
$expr=sprintf( '//HighwayRoutingData/tag[ contains( text(), "%s") ]', urldecode( $loc ) );
$col=$xp->query( $expr );
/* create the new symbol and add to the relevant `destinationSymbols` node */
/* Here is the code I referenced above.
if( $col && $col->length > 0 && !empty( $code ) && !in_array($code, $symbol)){
$symbol=$dom->createElement( 'string', $code );
$dest=$xp->query( 'destinationSymbols', $col->item(0)->parentNode )->item(0);
$dest->appendChild( $symbol );
}
}
/* save the XML and redirect? */
$dom->save( urldecode( $fileName ) );
}
?>
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8' />
<title>Simple XML Processing...</title>
</head>
<body>
<!--
a single form should be OK and there is no need to repeat the
`fileName` element as this is the same for each row in the table.
-->
<form method='post'>
<table border=1 cellpadding='5px' cellspacing='2px'>
<tr>
<th>Tag</th>
<th>Strings</th>
<th colspan=3> </th>
</tr>
<?php
$file='RouteSymbol.xml';
libxml_use_internal_errors( true ) ;
/*
Load the XML file and build the HTML output
*/
$dom=new DOMDocument();
$dom->validateOnParse=false;
$dom->recover=true;
$dom->strictErrorChecking=false;
$dom->load( $file );
libxml_clear_errors();
$xp=new DOMXPath( $dom );
$col=$xp->query('//HighwayRoutingData');
if( $col && $col->length > 0 ){
foreach( $col as $node ){
/*
find the `string` values and add to an array
which we will use to build a string later.
*/
$output=array();
$strings=$xp->query( 'destinationSymbols/string', $node );
foreach( $strings as $string )$output[]=$string->nodeValue;
/*
Find the tag for this node.
*/
$tag=$xp->query('tag',$node)->item(0)->nodeValue;
/*
Generate a new HTML table row with data found in this
node of the xml.
*/
printf('
<tr>
<td>%1$s</td>
<td>%2$s</td>
<td>
<input type="text" name="symbol[]" />
<input type="hidden" name="location[]" value="%1$s" />
</td>
<td><input type="submit" value="ADD" /></td>
<td><a href="#delete">Delete</a></td>
</tr>',
$tag,
implode( ', ', $output )
);
}
}
?>
</table>
<input type='hidden' name='fileName' value='<?=$filename;?>' />
</form>
</body>
</html>
<?xml version="1.0"?>
<ArrayOfHighwayRoutingData xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<HighwayRoutingData>
<tag>@I80</tag>
<destinationSymbols>
<string>SFO</string>
<string>OAK</string>
<string>EMR</string>
<string>ELC</string>
<string>RIC</string>
<string>SPB</string>
<string>HER</string><string>HER</string></destinationSymbols>
</HighwayRoutingData>
<HighwayRoutingData>
<tag>@SR24</tag>
<destinationSymbols>
<string>OAK</string>
<string>ORI</string>
<string>LFY</string>
<string>WCR</string>
</destinationSymbols>
</HighwayRoutingData>
<HighwayRoutingData>
<tag>@US101</tag>
<destinationSymbols>
<string>SFO</string>
<string>SSC</string>
<string>MIL</string>
<string>PAO</string>
</destinationSymbols>
</HighwayRoutingData>
</ArrayOfHighwayRoutingData>
CodePudding user response:
To prevent the duplication of STRING
tags within each HighwayRoutingData/destinationSymbols
nodes you need to query that node for strings containing the given text, as per the below modified extract:
foreach( $symbol as $index => $code ){
$loc=urldecode( $location[ $index ] );
$expr=sprintf( '//HighwayRoutingData/tag[ contains( text(), "%s") ]', urldecode( $loc ) );
$col=$xp->query( $expr );
if( $col && $col->length > 0 && !empty( $code ) ){
/* find the parent node based upon the Tag & the target node */
$parent=$col->item(0)->parentNode;
$dest=$xp->query('destinationSymbols', $parent )->item(0);
/* Query within the target node to find strings matching POSTed value */
$expr=sprintf('string[ contains( text(), "%s" ) ]',$code );
$exists = $xp->query( $expr, $dest );
/* Not found, add new string */
if( $exists && $exists->length > 0 ){
# it exists....
}else{
# it does not exist....
$symbol=$dom->createElement( 'string', $code );
$dest->appendChild( $symbol );
}
}
}