Thazer Posted September 30, 2021 Share Posted September 30, 2021 (edited) 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 September 30, 2021 by Thazer Quote Link to comment Share on other sites More sharing options...
gw1500se Posted September 30, 2021 Share Posted September 30, 2021 What have you tried and what doesn't work as you expect? Are you using XML Parser? Do you have errors turned on? error_reporting(E_ALL); Quote Link to comment Share on other sites More sharing options...
cyberRobot Posted September 30, 2021 Share Posted September 30, 2021 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. Quote Link to comment Share on other sites More sharing options...
Thazer Posted September 30, 2021 Author Share Posted September 30, 2021 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 ... Quote Link to comment Share on other sites More sharing options...
Barand Posted September 30, 2021 Share Posted September 30, 2021 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> Quote Link to comment Share on other sites More sharing options...
Thazer Posted September 30, 2021 Author Share Posted September 30, 2021 (edited) 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: - 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": - And how do i "hide" certain titles of header-like entity with id 1 (row. #1. & row #2.): 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 September 30, 2021 by Thazer Quote Link to comment Share on other sites More sharing options...
Thazer Posted October 1, 2021 Author Share Posted October 1, 2021 (edited) Made it to the point where i'm able to output comparison table almost as i need it to be: - 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\"> </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\"> </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 October 1, 2021 by Thazer typos Quote Link to comment Share on other sites More sharing options...
Solution Barand Posted October 1, 2021 Solution Share Posted October 1, 2021 (edited) Plan B <?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 .= " <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   <?=productOptions($feed)?>   <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 October 4, 2021 by Barand correct trailing </th> 1 1 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.