PHP XML to dynamic table

自古美人都是妖i 提交于 2019-12-02 09:39:55
hakre

I've given an extensive example in the question PHP simplexml xpath search for value in an ELEMENT containing tab delimited text? that shows how you can manipulate a SimpleXMLElement on-the-fly containing encoded data turning it into more accessible data.

This could be done similarly in your case, however this time I'd like to to show how this can be done with custom iterators.

But first let's consider how a table could be represented: A table consists of rows and columns. So we could iterate over rows and each row could be an iterator of columns.

And with your question, there is also another iterator over the tables. So in pseudo-code this can look like:

foreach($tables as $table)
{
    foreach ($table as $rows) {
        foreach ($rows as $column) {
        }
    }
}

Doing the output somewhere around that. So for example, if we have got an Iterator that is able to iterate over rows and columns that way to convert is into a table (for example as plain text for demonstration purposes so far), the pseudo-code can be reduced to this:

foreach($tables as $table)
{
    echo new TextTable($table);
}

As this example shows, the problem has been already reduced to display a single table - because all tables follow the same structure. I do not show TextTable works right now, it's not of further interest. If you are looking for an iterator that outputs a HTML table following the same iteration, see the linked question above, it has got a HtmlTableIterator which does most of the work. But right now, let's look how to create the iterator over rows and columns.

For that I introduce you to an existing iterator class named DecoratingIterator. It allows to decorate each current element in an iteration. This is pretty flexible because we can use it to compose the final table-iterator in a pretty flexible way in small steps.

Let's solve all the problems you face, step by step. First of all a function is needed to turn the number 0 into an empty string. Let's create a function for that (and I also add some more formatting by prefixing with spaces if necessary to get a length of two):

$formatNumber = function($number)
{
    if ($number === '0') {
        $number = '';
    }

    return sprintf("%' 2s", $number);
};

The next problem to solve is to turn the SimpleXMLElement of numbers into something foreach-able that are those formatted numbers. Let's just write it as well and use the previous function to help get it done:

$numbersToArray = function($stringNumbers) use ($formatNumber) {
    return array_map($formatNumber, explode('-', $stringNumbers));
};

Wow, that was short. The next problem to solve is to turn all rows of a single table into these array of numbers. Well, again, we do it with the help of the last function:

$tableToRows = function(SimpleXMLElement $table) use ($numbersToArray) {
    return new DecoratingIterator($table->children(), $numbersToArray);
};

And finally one problem is left: The whole XML needs to be turned into an iterator of all the tables. Again with the previous function this is more easy again:

$tables = new DecoratingIterator(
    $xml->message->ticketpage->ticket,
    $tableToRows
);

Wow. Probably a bit long, let's review:

$formatNumber = function($number)
{
    if ($number === '0') {
        $number = '';
    }

    return sprintf("%' 2s", $number);
};

$numbersToArray = function(SimpleXMLElement $stringNumbers) use ($formatNumber) {
    return array_map($formatNumber, explode('-', $stringNumbers));
};

$tableToRows = function(SimpleXMLElement $table) use ($numbersToArray) {
    return new DecoratingIterator($table->children(), $numbersToArray);
};

$tables = new DecoratingIterator(
    $xml->message->ticketpage->ticket,
    $tableToRows
);

All these lines of code do is to provide the $tables iterator we can use to display the tables. So to make this more easy to use, we wrap this into a class of it's own and signal PHP that this class can aggregate an iterator which works via the IteratorAggregate interface:

class TableAggregator implements IteratorAggregate
{
    private $xml;

    public function __construct(SimpleXMLElement $xml)
    {
        $this->xml = $xml;
    }

    public function getIterator() 
    {
        # ... code to create the tables iterator

        return $tables;
    }
}

Okay, that probably was a lot so far. Better show the usage-example now to make visible why aggregation makes sense here:

$xml = simplexml_load_file('example.xml');

$tables = new TableAggregator($xml);

foreach ($tables as $table) {
    echo new TextTable($table), "\n";
}

As this example shows, its because this is easy to use. Also if we have a different formatting needs, we could create a different aggregator - that simple it is. Let's see the exemplary output:

+--+--+--+--+--+--+--+--+--+
|  |10|27|30|45|  |  |  |80|
+--+--+--+--+--+--+--+--+--+
|  |15|  |38|  |51|62|  |85|
+--+--+--+--+--+--+--+--+--+
| 5|  |  |37|  |57|60|77|  |
+--+--+--+--+--+--+--+--+--+

+--+--+--+--+--+--+--+--+--+
|  |  |20|33|  |56|68|  |90|
+--+--+--+--+--+--+--+--+--+
| 8|  |  |  |49|  |64|71|84|
+--+--+--+--+--+--+--+--+--+
| 1|18|22|32|  |59|  |  |  |
+--+--+--+--+--+--+--+--+--+

+--+--+--+--+--+--+--+--+--+
|  |  |23|  |47|58|67|  |86|
+--+--+--+--+--+--+--+--+--+
| 4|16|  |  |43|53|  |  |88|
+--+--+--+--+--+--+--+--+--+
| 3|  |28|35|  |  |65|72|  |
+--+--+--+--+--+--+--+--+--+

+--+--+--+--+--+--+--+--+--+
|  |19|26|  |48|52|  |74|  |
+--+--+--+--+--+--+--+--+--+
|  |  |21|  |40|  |63|75|82|
+--+--+--+--+--+--+--+--+--+
| 9|11|  |34|41|  |  |76|  |
+--+--+--+--+--+--+--+--+--+

+--+--+--+--+--+--+--+--+--+
|  |12|29|36|44|  |  |78|  |
+--+--+--+--+--+--+--+--+--+
| 6|14|  |39|  |  |69|  |89|
+--+--+--+--+--+--+--+--+--+
| 2|  |  |  |  |54|66|70|81|
+--+--+--+--+--+--+--+--+--+

+--+--+--+--+--+--+--+--+--+
|  |17|25|  |46|  |  |73|87|
+--+--+--+--+--+--+--+--+--+
|  |  |24|  |42|50|61|79|  |
+--+--+--+--+--+--+--+--+--+
| 7|13|  |31|  |55|  |  |83|
+--+--+--+--+--+--+--+--+--+

The whole example at a glance: https://gist.github.com/hakre/5734770

VNTeck

You can try this:

<?php
$str = '<tickets xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <status>1</status>
            <message>
                <ticketpage>
                    <ticket>
                        <row>0-10-27-30-45-0-0-0-80</row>
                        <row>0-15-0-38-0-51-62-0-85</row>
                        <row>5-0-0-37-0-57-60-77-0</row>
                    </ticket>
                </ticketpage>
            </message>
        </tickets>';
$dom = new SimpleXMLElement($str);
foreach ($dom->message->ticketpage->ticket as $ticket) {
    echo '<table border=1>';
    foreach($ticket->row as $row){
        $cellList = explode('-',$row);
        echo '<tr>';
        foreach ($cellList as $cell){
            echo '<td>', $cell, '</td>';
        }
        echo '</tr>';
    }
    echo '</table>';
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!