Search code examples
phpxmlforeachsimplexml

How to get XML attribute values using PHP


I am trying to get value from xml attributes. but i am only getting 1st bookshelf stocks values but 2nd stocks value i am not getting. how i can show Empty when there is no stock value like in the book ID="5"

XML test.xml

<?xml version="1.0" encoding="utf-8"?>
<bookstore Code="111">
    <bookshelf  Date="01/26/2021" Time="7:35 PM" Note=" ">
        <book ID="3" Name="website1">
            <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/25/2021" Time="7:35 PM" Note=" ">
        <book ID="3" Name="website1"/>
        <book ID="4" Name="website 2"/>
        <book ID="7" Name="website 3"/>
        <book ID="5" Name="website 4" />
    </bookshelf>
</bookstore>

PHP CODE

<?php $mybooks = simplexml_load_file('test.xml');?>
<table width="100%" border="1">
  <tbody>
    <tr>
      <td>Bookshelf Date</td>
      <td>website1</td>
      <td>website2</td>
      <td>website3</td>
      <td>website4</td>
    </tr>
  <tr><td><?php  echo $bookshelf['Date']; ?></td>
      <?php
      foreach($mybooks->bookshelf->book as $d) {
      $d['Name'].' '.$d['ID'].'<br>';
      foreach( $d->stock as $stocks) {
      $stocks['price'].' '.$stocks['Name']."\n";
      ?>
      <td><?php  echo $stocks['price'].' '.$stocks['Name']."\n"; ?> </td>
     <?php }} ?></tr></tbody>
</table>

Updated: what if from one bookshelf -> book getting no data. how can i check it if that bookshelf getting data or hide it from showing it in result.


Solution

  • 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: ex1

    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: ex2

    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: ex3

    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 );
        }
        # output
        echo implode( PHP_EOL, $data );
        
        
        $mybooks = $data = $row = $cells = $books = $book = $shelf = null;
    ?>
    

    Which outpus: ex4