Jump to content

PHP parsing XML data and draw HTML Table


Thazer
 Share

Go to solution Solved by Barand,

Recommended Posts

Hi!

I'm new to this forum and i'm in need of assistance...

I have a case where i need to parse a "Products specifications" XML style document (data) that looks like this:

    <Message>
    <EntityType>Document</EntityType>
    <ProductSETS>
      <Entity>
       <id>1</id>
       <Specification1>Human readable title 1</Specification1>
       <Specification2>Human readable title 2</Specification2>
       <Price>Product Price</Price>
       [...more elements of same row amount...]
      </Entity>
    
      <Entity>
       <id>100</id>
       <Specification1>Red</Specification1>
       <Specification2>Square</Specification2>
       <Price>8888</Price>
       [...more elements of same row amount...]
      </Entity>
    
      <Entity>
       <id>101</id>
       <Specification1>Blue</Specification1>
       <Specification2>Round</Specification2>
       <Price>9999</Price>
       [...more elements of same row amount...]
      </Entity>
    
    </ProductSETS>
    </Message>

And in short i need to print a table that contains the XML data like:

_____________________________________________________________________
| ProductSET for ID 100                                             |
---------------------------------------------------------------------
| Human readable title 1                 |  Red                     |
| Human readable title 2                 |  Square                  |
| Product Price                          |  8888                    |
| Next element in human readable form    |  ID. 100 ELEMENT [...]   |
[   .... and loop until end of <Entity> <id>100</id>  ............. |
_____________________________________________________________________
| ProductSET for ID 101                                             |
---------------------------------------------------------------------
| Human readable title 1                 |  Blue                    |
| Human readable title 2                 |  Round                   |
| Product Price                          |  9999                    |
| Next element in human readable form    |  ID. 101 ELEMENT [...]   |
[   .... and loop until end of <Entity> <id>101</id>  ............. |
_____________________________________________________________________

Notice that the xml data has the first entity as a "header"-like entry where id number 1 is used to map the "Human readable" value of the products specifications titles and after the "header" entity the actual product specifications value are present (Color, Size, Price etc.) and i need to display a table in HTML with values from XML data - without entity with 1, but still use it's values for all the rest entities.

 

Edited by Thazer
Link to comment
Share on other sites

So...where are you stuck? Do you have a process for converting the XML data into something that PHP can loop through? If not, you may want to try PHP's SimpleXML extension. More information can be found here:
https://www.php.net/manual/en/book.simplexml.php

As for the human-readable headings, you could try looping through the "Entity" elements, looking for the one where the "id" is equal to "1". Then go on to process and output the remaining "Entity" elements.

If you have control over the XML code, it might be better to modify things so the headers are stored using unique tags. That way you can just grab them without needing to loop through all the "Entity" elements.

Link to comment
Share on other sites

I'm using the following code:

<!DOCTYPE html>
<html>
<head>
<style>
table, th, td {
  border: 1px solid black;
  border-collapse: collapse;
}
th, td {
  padding: 5px;
  text-align: left;
}
#vertical-2 thead,#vertical-2 tbody{
display:inline-block;
}
</style>
</head>
<body>

<?php
error_reporting(1);
ini_set('error_reporting', E_ALL);

$data = "
<Message>
    <EntityType>Document</EntityType>
    <ProductSETS>
      <Entity>
       <id>1</id>
       <Specification1>Name</Specification1>
       <Specification2>Color</Specification2>
       <Specification3>Type</Specification3>
       <Specification4>Manufacturer</Specification4>
       <Price>Product Price</Price>
      </Entity>
    
      <Entity>
       <id>100</id>
       <Specification1>Car model 1</Specification1>
       <Specification2>Red</Specification2>
       <Specification3>Hybrid car</Specification3>
       <Specification4>Telsa</Specification4>
       <Price>10000</Price>
       </Entity>
    
      <Entity>
       <id>101</id>
       <Specification1>Car model 2</Specification1>
       <Specification2>Blue</Specification2>
       <Specification3>Diesel car</Specification3>
       <Specification4>Ford</Specification4>
       <Price>5000</Price>
       </Entity>

      <Entity>
       <id>102</id>
       <Specification1>Car model 3</Specification1>
       <Specification2>Black</Specification2>
       <Specification3>SUV</Specification3>
       <Specification4>Hummer</Specification4>
       <Price>8000</Price>
       </Entity>
       
       
    </ProductSETS>
    </Message>
";


global $time_start;
$time_start = microtime(true); echo "start: ".date("H:i:s")."<br>\n";

global $time_start;
$time_start = microtime(true); echo "start: ".date("H:i:s")."<br>\n";

function print_mem()
{
   $mem_usage = memory_get_usage();
   $mem_peak = memory_get_peak_usage();
   echo 'The script is now using: <strong>' . round($mem_usage / 1024) . 'KB</strong> of memory.<br>';
   echo 'Peak usage: <strong>' . round($mem_peak / 1024) . 'KB</strong> of memory.<br><br>';
}

$feed = new DOMDocument('1.0', 'utf-8');
$feed = simplexml_load_string($data);

?>


	<table id="vertical-2">
	<caption>Products</caption>

<tbody>
<?php foreach ($feed->ProductSETS->Entity as $ElementERP) :?>

<?php if($ElementERP->id == "1"){ ?>
<tr>
<td><?php echo $ElementERP->Specification1; ?></td>
<td><?php echo $ElementERP->Specification2; ?></td>
<td><?php echo $ElementERP->Specification3; ?></td>
<td><?php echo $ElementERP->Specification4; ?></td>
<td><?php echo $ElementERP->Price; ?></td>
</tr>
<?php } ?>

<?php if($ElementERP->id != "1"){ ?>
<tr>
<td><?php echo $ElementERP->Specification1; ?></td>
<td><?php echo $ElementERP->Specification2; ?></td>
<td><?php echo $ElementERP->Specification3; ?></td>
<td><?php echo $ElementERP->Specification4; ?></td>
<td><?php echo $ElementERP->Price; ?></td>
</tr>
<?php } ?>
<?php endforeach; ?>
</tbody>
	<tfoot>
	<tr>
	<td colspan="4">End of list</td>
	</tr>
	</tfoot>
	</table>
  
</body>
</html>

And the output is :

Name 	        Color 	Type 	    	Manufacturer 	Product Price
Car model 1 	Red 	Hybrid car 	Telsa 	        10000
Car model 2 	Blue 	Diesel car 	Ford 	        5000
Car model 3 	Black 	SUV 	    	Hummer 	        8000

and i need it to be horizontal-comparison layout and still be in a loop because i have multiple xml sources with different amout and types of "specifications" and i'd like something where the code is something more automated and i don't have to map each xml source, with lots of $ElementERP->SpecificationXXX , and in the end result would be:

Name		Car model 1		Car model 2 		Car model 3
Color		Red			Blue			Black
Type		Hybrid car		Diesel car		SUV
Manufacturer	Telsa			Ford			Hummer
Product Price	10000			5000			8000

So with multiple $data xml strings, and an variable amout of "specifications" with one php script to output html comparison-type tables, and not an script for each $data xml string ...

Link to comment
Share on other sites

Something like this?

<?php
$data = "
<Message>
    <EntityType>Document</EntityType>
    <ProductSETS>
      <Entity>
       <id>1</id>
       <Specification1>Name</Specification1>
       <Specification2>Color</Specification2>
       <Specification3>Type</Specification3>
       <Specification4>Manufacturer</Specification4>
       <Price>Product Price</Price>
      </Entity>
    
      <Entity>
       <id>100</id>
       <Specification1>Car model 1</Specification1>
       <Specification2>Red</Specification2>
       <Specification3>Hybrid car</Specification3>
       <Specification4>Telsa</Specification4>
       <Price>10000</Price>
       </Entity>
    
      <Entity>
       <id>101</id>
       <Specification1>Car model 2</Specification1>
       <Specification2>Blue</Specification2>
       <Specification3>Diesel car</Specification3>
       <Specification4>Ford</Specification4>
       <Price>5000</Price>
       </Entity>

      <Entity>
       <id>102</id>
       <Specification1>Car model 3</Specification1>
       <Specification2>Black</Specification2>
       <Specification3>SUV</Specification3>
       <Specification4>Hummer</Specification4>
       <Price>8000</Price>
       </Entity>
       
       
    </ProductSETS>
    </Message>
";
$feed = simplexml_load_string($data);

$out = '';
foreach ($feed->ProductSETS->Entity as $prod) {
    $out .= "<div class='product'>\n";
    foreach ($prod as $k => $spec) {
        if ($k != 'id') $out .= "$spec<br>";
    }
    $out .= "</div>\n";
} 
?>
<!DOCTYPE html>
<html lang='en'>
<head>
<title>Example</title>
<meta charset='utf-8'>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script type='text/javascript'>
    $().ready( function() {
        
        $("#find").click( function() {
            var tid = $("#tid").val()
            $.get(
                "",                                                // specify processing file on server (in this case it's same file)
                {"ajax":"teacher", "tid":tid},                     // data to send in request
                function(resp) {                                   // handle the reponse
                    $.each(resp, function(k,v) {
                        $("#"+k).val(v)
                    })
                },                                                  
                "JSON"                                              // response type
            )
        })
    })
</script>
<style type='text/css'>
    body {
        font-family: calibri, sans-serif;
        font-size: 12pt;
    }
    div.product {
        margin: 16px;
        padding: 8px;
        width: 200px;
        float: left;
    }
    
</style>
</head>
<body>

    <?= $out ?>

</body>
</html>

image.png.ef7cbfde0d06ba885335f5493babcec3.png

Link to comment
Share on other sites

1 hour ago, Barand said:

Something like this?

WoW ! Amazing ! it indeed outputs a comparison-type table and works perfectly with the sample data i've posted but in my quest to get help i've made a mistake of stripping demo $data and when using actual xml that i need to process, the output has the following issues:

- Due to using divs, each element height may be different because of text length and if so it breaks <table> inline-rows-style (even blank specifications like <Specification4/> in the code sample attached to the end of this post:

image.thumb.png.1ebb443a63492be44d2336e7b8f2e0da.png

- Also due to my negligence in the initial forum-thread i've forgot to mention that i also need to make a filter to show only one product with the titles set by entity id number 1, like if there's a filter set say like "show only specs for product that matches ID number 102":

image.png.8db936887bc15d8c00b554f46380b3be.png

- And how do i "hide" certain titles of header-like entity with id 1 (row. #1. & row #2.):

image.png.71bd8b5ce494d05c21510e467da689c4.png

because if i

if ($k != 'id' && $spec == "1")

it hides any spec that has the value 1 (like in the above screenshot example price is 1, but was looking to hide only row 1 and 2 of first div...)

so so sorry for my mistakes and negligence, also this is my current code with more precise xml $data:

<?php
$data = "
<Message>
    <EntityType>Document</EntityType>
    <ProductSETS>
      <Entity>
       <id>1</id>
       <sku>1</sku>
       <ean>1</ean>
       <Specification1>Name</Specification1>
       <Specification2>Color</Specification2>
       <Specification3>Type</Specification3>
       <Specification4>Manufacturer</Specification4>
       <Price>Product Price</Price>
      </Entity>
    
      <Entity>
       <id>100</id>
       <sku>prod-0001</sku>
       <ean>5901234123456</ean>
       <Specification1>Lorem ipsum</Specification1>
       <Specification2>Red</Specification2>
       <Specification3>Hybrid car</Specification3>
       <Specification4>Telsa</Specification4>
       <Price>10000</Price>
       </Entity>
    
      <Entity>
       <id>101</id>
       <sku>prod-0002</sku>
       <ean>5907171819916</ean>
       <Specification1>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia</Specification1>
       <Specification2>Blue</Specification2>
       <Specification3>
       Diesel car
       </Specification3>
       <Specification4/>
       <Price>5000</Price>
       </Entity>

      <Entity>
       <id>102</id>
       <sku>prod-0003</sku>
       <ean>6189192737718</ean>
       <Specification1>Ut enim ad minim veniam quis nostrud exercitation</Specification1>
       <Specification2>Black</Specification2>
       <Specification3>SUV</Specification3>
       <Specification4>Hummer</Specification4>
       <Price>1</Price>
       </Entity>
       
       
    </ProductSETS>
    </Message>
";
$feed = simplexml_load_string($data);

$out = '';
$count = 0;
foreach ($feed->ProductSETS->Entity as $prod) {
    $out .= "<div class='product'>\n";
    foreach ($prod as $k => $spec) {
        if ($k != 'id') { $count++; $out .= "#".$count.". ".$spec."<hr>";}
    }
    $out .= "<br>".$count."rows</div>\n";
    $count = 0;
} 
?>
<!DOCTYPE html>
<html lang='en'>
<head>
<title>Example</title>
<meta charset='utf-8'>
<style type='text/css'>
    body {
        font-family: calibri, sans-serif;
        font-size: 12pt;
    }
    div.product {
        margin: 16px;
        padding: 8px;
        width: 200px;
        float: left;
        display: inline-block;
    }

</style>
</head>
<body>

    <?= $out ?>

</body>
</html>

 

Edited by Thazer
Link to comment
Share on other sites

Made it to the point where i'm able to output comparison table almost as i need it to be:

image.thumb.png.7e8354dae32e87f30c2689f20edfa669.png

- counting rows is just for debug purposes
- first div with data from entry id. 1 has been "cleaned" removing "1" standalone character from first 3 elements
- row numbering is just for debug purposes

can't find the right way to add a "$filter" when it's used to show two columns 1 column to be data formated from entry id. 1 and the next column to match $prod->id == $format ... can't really wrap my head around if()'s :(

latest code:

<?php
$data = "
<Message>
    <EntityType>Document</EntityType>
    <ProductSETS>
      <Entity>
       <id>1</id>
       <sku>1</sku>
       <ean>1</ean>
       <Specification1>Name</Specification1>
       <Specification2>Color</Specification2>
       <Specification3>Type</Specification3>
       <Specification4>Manufacturer</Specification4>
       <Price>Product Price</Price>
      </Entity>
    
      <Entity>
       <id>101</id>
       <sku>prod-0001</sku>
       <ean>5901234123456</ean>
       <Specification1>Lorem ipsum</Specification1>
       <Specification2>Red</Specification2>
       <Specification3>Hybrid car</Specification3>
       <Specification4>Telsa</Specification4>
       <Price>10000</Price>
       </Entity>
    
      <Entity>
       <id>102</id>
       <sku>prod-0002</sku>
       <ean>5907171819916</ean>
       <Specification1>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia</Specification1>
       <Specification2>Blue</Specification2>
       <Specification3>
       Diesel car
       </Specification3>
       <Specification4/>
       <Price>5000</Price>
       </Entity>

       <Entity>
       <id>103</id>
       <sku>prod-0003</sku>
       <ean>6189192737718</ean>
       <Specification1>veniam quis nostrud exercitation</Specification1>
       <Specification2>Black</Specification2>
       <Specification3>SUV</Specification3>
       <Specification4>Hummer</Specification4>
       <Price>1</Price>
       </Entity>

       <Entity>
       <id>104</id>
       <sku>prod-0004</sku>
       <ean>y0y</ean>
       <Specification1>nostrud exercitation</Specification1>
       <Specification2>Black</Specification2>
       <Specification3>SUV</Specification3>
       <Specification4>Hummer</Specification4>
       <Price>1</Price>
       </Entity>
       
       <Entity>
       <id>105</id>
       <sku>prod-0005</sku>
       <ean>x0x</ean>
       <Specification1/>
       <Specification2/>
       <Specification3/>
       <Specification4/>
       <Price>1</Price>
       </Entity>
           
       
    </ProductSETS>
    </Message>
";
$feed = simplexml_load_string($data);

$out = '';
$count = 0;
foreach ($feed->ProductSETS->Entity as $prod) {
    $out .= "<div class='product'>\n";
    foreach ($prod as $k => $spec) {
        if ($k != 'id') {
       if ($prod->id == "1" && $spec == "1"){
        $count++; $out .= "<div class=\"rowBG\">&nbsp;</div>";
       }else{
        $count++; $out .= "<div class=\"rowBG\">".$count.". ".$spec."</div>";
       }
     }
    }
    $out .= "<br>".$count."rows</div>\n";
    $count = 0;
} 
?>
<!DOCTYPE html>
<html lang='en'>
<head>
<title>Example</title>
<meta charset='utf-8'>
<style type='text/css'>
    body {
        font-family: calibri, sans-serif;
        font-size: 12pt;
    }
.main{
width:2480px;
overflow-x: auto;
border: 1px solid blue;
}
    div.product {
/*        margin: 16px; */
        padding-right: 1px;
/*        float: left; */
        display: inline-block;
    }
.rowBG:nth-last-child(2n+1) {
background: #ffffff;
}

.rowBG:nth-last-child(2n) {
background: #f2f3f4;
}

</style>
</head>
<body>

    <?= $out ?>

</body>
</html>

added filter code:

$filter = "102"; //will be replaced by "$_GET"
if(empty($filter)){ $filterByID = "NO"; } else { $filterByID = $filter; }

foreach ($feed->ProductSETS->Entity as $prod) {

if($filterByID == "NO"){
//If there's no filter set show all products
    $out .= "<div class='product'>\n";
    foreach ($prod as $k => $spec) {
        if ($k != 'id') {
       if ($prod->id == "1" && $spec == "1"){
        $count++; $out .= "<div class=\"rowBG\">&nbsp;</div>";
       }else{
        $count++; $out .= "<div class=\"rowBG\">".$count.". ".$spec."</div>";
       }
     }
    }
    $out .= "</div>\n";
    $count = 0;

} elseif($prod->id == $filterByID) {
//But if there's a filter, show ID no.1 + ID==Fiter
    $out .= "<div class='product'>\n";
    foreach ($prod as $k => $spec) {
        if ($k != 'id') {
       if ($prod->id == $filterByID){
        $count++; $out .= "<div class=\"rowBG\">".$count.". ".$spec."</div>";
       }
     }
    }
    $out .= "</div>\n";
    $count = 0;
} else {
die('No matching filter criteria, please check filter');
}

}

stuck... and i think my approach is like a "butcher" - maybe there's a simpler way do to what i want... if there's no $filter show all xml data in comparison table, if $filter is set and it matches an id from xml entries show column 1 (xml entry id.1 ) + matched id column.

Edited by Thazer
typos
Link to comment
Share on other sites

  • Solution

Plan B

image.png.8651ac12bc9abef1bcf4a7fe1065922b.png

 
<?php
$xml = "
<Message>
    <EntityType>Document</EntityType>
    <ProductSETS>
      <Entity>
       <id>1</id>
       <sku>1</sku>
       <ean>1</ean>
       <Specification1>Name</Specification1>
       <Specification2>Color</Specification2>
       <Specification3>Type</Specification3>
       <Specification4>Manufacturer</Specification4>
       <Price>Product Price</Price>
      </Entity>
    
      <Entity>
       <id>100</id>
       <sku>prod-0001</sku>
       <ean>5901234123456</ean>
       <Specification1>Lorem ipsum</Specification1>
       <Specification2>Red</Specification2>
       <Specification3>Hybrid car</Specification3>
       <Specification4>Tesla</Specification4>
       <Price>10000</Price>
       </Entity>
    
      <Entity>
       <id>101</id>
       <sku>prod-0002</sku>
       <ean>5907171819916</ean>
       <Specification1>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia</Specification1>
       <Specification2>Blue</Specification2>
       <Specification3>Diesel car</Specification3>
       <Specification4>Trabant</Specification4>
       <Price>5000</Price>
       </Entity>

      <Entity>
       <id>102</id>
       <sku>prod-0003</sku>
       <ean>6189192737718</ean>
       <Specification1>Ut enim ad minim veniam quis nostrud exercitation</Specification1>
       <Specification2>Black</Specification2>
       <Specification3>SUV</Specification3>
       <Specification4>Hummer</Specification4>
       <Price>1</Price>
       </Entity>
       
       
    </ProductSETS>
</Message>
";
$chosen = $_GET['choice'] ?? [];

$feed = simplexml_load_string($xml);
$heads = $prods = [];
$tdata = '';

if ($chosen) {
    $h = $feed->xpath('//Entity[id = 1]')[0];         // headings from entity #1
    $p = $feed->xpath('//Entity[id > 1]');            // products from rest of entities
    foreach ($h as $k=>$v) {
        $heads[$k] = (in_array($k, ['id','sku','ean'])) ? '' : (string)$h->$k;
        if (!isset($prods[$k])) {
            $prods[$k] = [ 'heading' => $heads[$k],
                           'specs'   => []
                         ];
        }
    }
    foreach ($p as $spec) {
        if (!in_array((string)$spec->id, $chosen)) continue;
        foreach ($spec as $k=>$s) {
            $prods[$k]['specs'][] = trim((string)$s);
        }
    }

    foreach ($prods as $k => $row) {
        if ($k=='id') continue;
        $tdata .= "<tr><th>{$row['heading']}</th>";
        $tdata .= '<td>' . join('</td><td>', $row['specs']) . "</td></tr\n";
    }
}
/**
* build list of checkbox options for product selection
* 
* @param simplexmlobj $feed
*/
function productOptions($feed) 
{
    $prods = $feed->xpath('//Entity[id > 1]');
    $opts = '';
    foreach ($prods as $p) {
        $opts .= "&emsp;<label><input type='checkbox' name='choice[]' value='{$p->id}'> {$p->sku}</label>\n";
    }
    return $opts;
}
?>
<!DOCTYPE html>
<html lang='en'>
<head>
<title>Example</title>
<meta charset='utf-8'>
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
<style type='text/css'>
    td {
        width: 20%;
        padding: 8px;
    }
    th {
        width: 20%;
        padding: 8px;
        text-align: left;
        background-color: black;
        color: white;
    }
</style>
</head>
<body>
<div class="w3-container w3-blue-gray w3=padding">
    <h1>Product Comparison</h1>
</div>
<form class="w3-container w3-light-gray w3-padding-24">
    Select products to compare &emsp; <?=productOptions($feed)?>
    &emsp;
    <button class='w3-button w3-blue w3-round'>Compare</button>
</form>   
<div class="w3-continer w3-responsive w3-padding w3-margin-top">
    <table class="w3-table-all">
        <?= $tdata ?>
    </table>
</div>
</body>
</html>

 

 

Edited by Barand
correct trailing </th>
  • Like 1
  • Great Answer 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.