I offer to you 3 methods to accomplish your end goal - admittedly only one uses simple_xml
Method #1: Simple XML
As you wish to display ALL websites per row in the table you need nested queries. The outer query to return the rows ( bookshelfs ) and the inner query to return the books on that bookshelf. When it comes to the stock
node that does not exist for particular books I used a ternary operator to select another value if the stock
tag did not exist.
<?php
$xmlfile='test.xml';
$mybooks = simplexml_load_file( $xmlfile );
?>
<table border=1 cellspacing=5 cellpadding=5>
<thead>
<tr>
<th>Bookshelf Date</th>
<th>Website 1</th>
<th>Stock</th>
<th>Website 2</th>
<th>Stock</th>
<th>Website 3</th>
<th>Stock</th>
<th>Website 4</th>
<th>Stock</th>
</tr>
</thead>
<tbody>
<?php
foreach( $mybooks->bookshelf as $shelf ) {
?>
<tr>
<td><?php echo $shelf['Date'];?></td>
<?php
foreach( $shelf->book as $book ){
?>
<td><?php echo $book['Name'];?></td>
<td>
<?php
$stock=$book->stock ? $book->stock['price'] : 'None';
echo $stock;
?>
</td>
<?php
}
?>
</tr>
<?php
}
?>
</tbody>
</table>
Which outputs:
Method #2: DOMDocument & XPath
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8' />
<title></title>
</head>
<body>
<table border=1 cellspacing=5 cellpadding=5>
<tr>
<th>Bookshelf Date</th>
<th>Website 1</th>
<th>Stock</th>
<th>Website 2</th>
<th>Stock</th>
<th>Website 3</th>
<th>Stock</th>
<th>Website 4</th>
<th>Stock</th>
</tr>
<?php
$xml='<?xml version="1.0"?>
<bookstore Code="111">
<bookshelf Date="01/26/2021" Time="7:35 PM" Note=" ">
<book ID="3" Name="website 1">
<stock price="10" Name="Name5"/>
</book>
<book ID="4" Name="website 2">
<stock price="12" Name="Name2"/>
</book>
<book ID="7" Name="website 3">
<stock price="10.50" Name="Name3" />
</book>
<book ID="5" Name="website 4" />
</bookshelf>
<bookshelf Date="01/26/2021" Time="7:35 PM" Note=" ">
<book ID="3" Name="website 1">
<stock price="110" Name="Name5"/>
</book>
<book ID="4" Name="website 2">
<stock price="112" Name="Name2"/>
</book>
<book ID="7" Name="website 3">
<stock price="110.50" Name="Name3" />
</book>
<book ID="5" Name="website 4" />
</bookshelf>
</bookstore>';
$dom=new DOMDocument;
$dom->loadXML( $xml );
$xp=new DOMXPath( $dom );
$expr='//bookshelf';
$col=$xp->query( $expr );
if( $col->length > 0 ){
foreach( $col as $shelf ){
$tmp=[ $shelf->getAttribute('Date') . ' ' . $shelf->getAttribute('Time') ];
$bookcol=$xp->query('book', $shelf);
if( $bookcol->length > 0 ){
foreach( $bookcol as $book ){
$stock=$xp->query('stock', $book );
$stock=( $stock->length > 0 ) ? $stock->item(0)->getAttribute('price') : 'None';
$tmp[]=$book->getAttribute('Name');
$tmp[]=$stock;
}
}
printf('
<tr>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
</tr>',
...$tmp
);
}
}
?>
</table>
</body>
</html>
Which outputs:
Method #3: DOM Document & XSLTProcessor - XSL Stylesheets
The XSL stylesheet
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- we want to output a portion of html -->
<xsl:output method='html' standalone='yes' indent='yes' encoding='utf-8'/>
<xsl:template match="/">
<style>
table.xml{
border:1px solid gray;
display:table;
border-collapse:collapse;
border-spacing:0px;
empty-cells:show;
table-layout:auto;
font-family:Verdana, Geneva, Arial, Helvetica, sans-serif;
font-size:0.8rem;
}
table.xml tr:first-of-type{
background:#2f4f4f;
color:white;
}
table.xml tr:nth-of-type( even ) td{
background:aliceblue;
}
table.xml td{
margin:0;
padding:5px;
border-bottom:1px dotted rgba(133,133,133,0.5);
}
</style>
<table class='xml' border='1' cellspacing='5' cellpadding='5'>
<tr>
<th>Bookshelf Date</th>
<th>Website 1</th>
<th>Stock</th>
<th>Website 2</th>
<th>Stock</th>
<th>Website 3</th>
<th>Stock</th>
<th>Website 4</th>
<th>Stock</th>
</tr>
<xsl:for-each select="bookstore/bookshelf">
<tr>
<xsl:attribute name='data-id'>
<xsl:value-of select="book/@ID"/>
</xsl:attribute>
<td><xsl:value-of select="@Date"/></td>
<xsl:for-each select="book">
<td><xsl:value-of select="@Name"/></td>
<td>
<xsl:attribute name='data-name'>
<xsl:value-of select="stock/@Name"/>
</xsl:attribute>
<xsl:choose>
<xsl:when test="stock/@price !=''">
<xsl:value-of select="stock/@price"/>
</xsl:when>
<xsl:otherwise>
None
</xsl:otherwise>
</xsl:choose>
</td>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
and the PHP to load and process it
<?php
$xmlfile='c:/wwwroot/xml/bookstore.xml';
$xslfile='c:/wwwroot/xsl/bookstore.xsl';
$dom=new DOMDocument;
$dom->load( $xmlfile );
$css=new DOMDocument;
$css->load( $xslfile );
$xsl=new XSLTProcessor;
$xsl->importStyleSheet( $css );
$html=$xsl->transformToXML( $dom );
header('Content-Type: text/html');
exit( $html );
?>
Which outputs:
update
Based upon modified XML and comment above:
<?php
$data=[];
foreach( $mybooks->bookshelf as $shelf ) {
$row=[];
$books=$shelf->children();
if( !empty( $books ) ){
if( $books->children() && count( $books->children() ) > 0 ){
$row[]=sprintf('<tr data-date="%s" data-time="%s" data-note="%s">', $shelf['Date'], $shelf['Time'], $shelf['Note'] );
$row[]=sprintf('<td>%s</td>',implode(', ', [ $shelf['Date'], $shelf['Time'] ] ) );
$cells=[];
foreach( $books as $book ){
if( $book->stock ){
$cells[]=sprintf('<td>%s</td>', $book['Name'] );
$cells[]=sprintf('<td>%s</td>',$book->stock['price']);
}
}
$row[]=implode( PHP_EOL, $cells );
$row[]='</tr>';
}
}
#append
$data[]=implode( PHP_EOL, $row );