Amend PHP with Xpath sorting

Hi

I’ve been trying to add some sort of Xpath to sort my XML with PHP, seems to be returning the correct amount of elements based on the criteria, but now no values are showing. No hotel name, no img, just empty div containers.

Can anybody spot what is causing the empty values, what am I doing wrong?

<?php $i=1; foreach ($hotels->hotel->xpath('//hotel/location[. ="some location"]') as $hotel): ?>
              <?php $clss= ($i % 3 == 0)? 'class="last"':'' ?>
              <div <?php echo $clss ?> >
                    <img src="images/<? echo $hotel->img->name ?>">
                    <h3><?php echo $hotel->name ?></h3>
                    <p><?php echo $hotel->description ?></p>
                    <p><b>Rating:</b> <?php echo $hotel->star ?></p>
                    <p><b>Location:</b> <?php echo $hotel->location ?></p>
            </div>
            <? $i++;?>
            <? endforeach ?>

If I use the below everything works ok but no Xpath sorting.

<?php $i=1; foreach ($hotels->hotel as $hotel): ?>

Barry

Can anybody shed some light on this?
50 viewings no reply :confused:

I might revert back to using a MySQL DB and leave extracting my data from XML altogether.
Any views on using MySQL over XML?

I was hoping to get a better understanding of using XML and querying this with Xpath as this is just a small project I’m working on, creating a full DB seems a bit overkill.

Barry

There are two things I’d do to try and sort out your problem,

  1. output var_dump($hotels->hotel->xpath(‘//hotel/location[. =“some location”]’)); and post it here, I’m curious as to what that returns
  2. output var_dump($hotels->hotel) and compare it to the output of #1

However, regardless, XML is a terrible data source to use in real time, it requires I/O operations were are slower than database interactions. The only time I ever use XML from an I/O standpoint is in nightly jobs that then process the data from the XML file and store that data in other formats (serialized json array, database, etc) anything that makes it more convenient for PHP to use.

Cheers cpradio

var_dump($hotels->hotel->xpath(‘//hotel/location[. =“some location”]’));

array(6) {
  [0]=>
  object(SimpleXMLElement)#5 (0) {
  }
  [1]=>
  object(SimpleXMLElement)#6 (0) {
  }
  [2]=>
  object(SimpleXMLElement)#4 (0) {
  }
  [3]=>
  object(SimpleXMLElement)#2 (0) {
  }
  [4]=>
  object(SimpleXMLElement)#3 (0) {
  }
  [5]=>
  object(SimpleXMLElement)#10 (0) {
  }
}

var_dump($hotels->hotel);

object(SimpleXMLElement)#8 (6) {
  ["img"]=>
  object(SimpleXMLElement)#6 (2) {
    ["name"]=>
    string(26) "hotel_one.jpg"
    ["alt"]=>
    string(12) "hotel one"
  }
  ["name"]=>
  string(6) "hotel1"
  ["description"]=>
  string(23) "Accommodation in the UK"
  ["address"]=>
  object(SimpleXMLElement)#5 (3) {
    ["number"]=>
    string(1) "3"
    ["street"]=>
    string(10) "steers way"
    ["postcode"]=>
    string(6) "W1 8LW"
  }
  ["star"]=>
  string(1) "4"
  ["location"]=>
  string(2) "uk"
}

However, regardless, XML is a terrible data source

I respect that and kind of agree but was hoping to get an understand of this, lots of jobs require XML data manipulation using Xpath and I have worked in previous roles (Interwoven Teamsite CMS in particular) which uses XML as its main data storage and styled using XSL transformations.

What do you think of the above, whats causing the issue?

And would you say json is the better option over XML?

Barry

Okay, I think your Xpath is incorrect. As you are only getting the location element returned.

Try this:

$hotels->hotel->xpath('//hotel[descendant::location[. ="some location"]]')

Place that in the var_dump as well to see what that gives.

Cool :slight_smile:

That works brilliantly.
The var_dump is array(6) now with all 6 hotels details as the above output, looks good thank you.

Now my next problem :cool:
How do I now sort these hotels by star rating?
And what if the value is budget and not a integer?

Cheers Barry

xpath alone can’t do sorting. That has to be done via XSL or you have to loop through and sort manually. What you have selectively done up to this point is filter the data.

I haven’t done a lot of sorting via XSL, again because it seems cumbersome to me (don’t get me wrong, I love XML over CSV, flat files, etc, but where you are heading with it seems to be more fitted for a relational database).

You can check out http://stackoverflow.com/questions/4848809/how-to-sort-values-via-xpath

You will see that you can sort across multiple elements and you get to define the data type of the element (so it may be a string, but if it is numeric, you can convert it to a number). The select attribute seems to define the element name to use, then there is the data-type and lastly the order.

If you mock something up and it isn’t doing what you want, post it and I’ll be glad to look it over and see if I spotted any mistakes :slight_smile: You can find an example in the comments for applying XSL to SimpleXMLElement at http://php.net/manual/en/book.xsl.php

Edit:

Added link to PHP manual where the comments show how to apply an XSL

Cheers cpradio

I haven’t done much XSL myself only worked with other peoples code and modified small snippets etc.
And yes that’s my predicament right now - to use a mysql db and be done with it, I can easily get the results I’m after but, ha I was hoping to learn something new and enhance my skillset and did thing this was a good way to do things.

Thanks for the link, I think I’ll review what works best for this small project and see where it takes me.
Cheers again for the help and I’ll be sure to post any further code for review if I come unstuck as I move forward.

Barry

If XSLT looks like too much hard work, remember that SimpleXML’s xpath() returns an array and PHP has several functions to sort arrays in a variety of ways. Here’s an example using [usort()](http://php.net/usort).


$hotels_at_location = $hotels->xpath('hotel[location="some location"]');
usort($hotels_at_location, function ($a, $b) { return (int) $b->star - (int) $a->star; });

foreach ($hotels_at_location as $hotel) {
    // Do something useful here
}