1

I'm sure there's a pretty obvious solution to this problem...but it's alluding me.

I've got an XML feed that I want to pull information from - from only items with a specific ID. Let lets say we have the following XML:

<XML>
   <item>
      <name>John</name>
      <p:id>1</id>
      <p:eye>Blue</eye>
      <p:hair>Black</hair>
   </item>
   <item>
      <name>Jake</name>
      <p:id>2</id>
      <p:eye>Hazel</eye>
      <p:hair>White</hair>
   </item>
   <item>
      <name>Amy</name>
      <p:id>3</id>
      <p:eye>Brown</eye>
      <p:hair>Yellow</hair>
   </item>
   <item>
      <name>Tammy</name>
      <p:id>4</id>
      <p:eye>Blue</eye>
      <p:hair>Black</hair>
   </item>
   <item>
      <name>Blake</name>
      <p:id>5</id>
      <p:eye>Green</eye>
      <p:hair>Red</hair>
   </item>
</xml>

And I want to pull ONLY people with the ID 3 and 1 into specific spots on a page (there will be no double IDs - unique IDs for each item). Using SimpleXML and a forloop I can easily display each ITEM on a page using PHP - with some "if ($item->{'id'} == #)" statements (where # is the ID I'm looking for(, I can also display the info for each ID I'm looking for.

The problem I'm running into is how to distribute the information across the page. I'm trying to pull the information into specific spots on a page my first attempt at distributing the specific fields across the page aren't working as follows:

<html>
<head><title>.</title></head>
<body>
<?php

(SimpleXML code / For Loop for each element here...)
?>

<H1>Staff Profiles</h1>
<h4>Maintenance</h4>
<p>Maintenance staff does a lot of work! Meet your super maintenance staff:</p>
<?php 
if($ID == 1) {
    echo "Name:".$name."<br/>";
    echo "Eye Color:".$eye."<br/>";
    echo "Hair Color:".$hair."<br/>";
?>

<h4>Receptionists</h4>
<p>Always a smiling face - meet them here:</p>
<?php 
if($ID == 3) {
    echo "Name:".$name."<br/>";
    echo "Eye Color:".$eye."<br/>";
    echo "Hair Color:".$hair."<br/>";
?>

<H4>The ENd</h4>

<?php (closing the four loop) ?>
</body>
</html>

But it's not working - it randomly starts repeating elements on my page (not even the XML elements). My method is probably pretty...rudimentary; so a point in the right direction is much appreciated. Any advice?

EDIT:

New (NEW) XPATH code:

 $count = 0;
 foreach ($sxe->xpath('//item') as $item) {

 $item->registerXPathNamespace('p', 'http://www.example.com/this');
 $id = $item->xpath('//p:id');

 echo $id[$count] . "\n";
 echo $item->name . "<br />";

 $count++;
 }
Cody S
  • 87
  • 2
  • 10
  • `xpath` will return an `array`, to get to that single id, you have to access `$id[0]`, or with PHP >= 5.4, go: `$id = $item->xpath("//p:id")[0];` – michi Nov 15 '13 at 11:36

2 Answers2

1

use xpath to accomplish this, and write a small function to retrieve a person by id.

function getPerson($id = 0, &$xml) {
    return $xml->xpath("//item[id='$id']")[0]; // PHP >= 5.4 required
}

$xml = simplexml_load_string($x); // assume XML in $x

Now, you can (example 1):

echo getPerson(5, $xml)->name;

Output:

Blake

or (example 2):

$a = getPerson(2, $xml);
echo "$a->name has $a->eye eyes and $a->hair hair.";

Output:

Jake has Hazel eyes and White hair.

see it working: http://codepad.viper-7.com/SwLids

EDIT In your HTML, this would probably look like this:

...
<h1>Staff Profiles</h1>
<h4>Maintenance</h4>
<p>Maintenance staff does a lot of work! Meet your super maintenance staff:</p>
<?php 
    $p = getPerson(4, $xml);
    echo "Name: $p->name <br />";
    echo "Eye Color: $p->eye <br />";
    echo "Hair Color: $p->hair <br />";
?>

no looping required, though.

michi
  • 6,565
  • 4
  • 33
  • 56
  • Ack - yes; I made up the example XML just to simplify my needs. In retrospect I should have just used what I was working with - because it gets a bit more complicated. I edited the original XML to include how the document I'm working with is structured - I can figure out converting it all from simpleXML to XPATH well enough, but I can't get how to snag the nodes with prefixed namespaces (n:blah). Any advice? – Cody S Nov 13 '13 at 21:01
  • @CodyS see here: http://www.php.net/manual/en/simplexmlelement.registerxpathnamespace.php – michi Nov 13 '13 at 21:46
  • Thanks. Progress, but another sticking point is that I can't get it to report the SINGLE id for each , instead it returns "ARRAY". For simplicity's sake (and to clarify my understanding of xpath and prefix namespaces) lets say I just want to report every person by ID and name. Right now my foreach returns "ARRAY Jim", etc. A loop inside the loop returns ALL the IDs and not just each individual ID. I've edited my code again to show what I've got. – Cody S Nov 14 '13 at 19:27
  • @CodyS see my comment to your question above! – michi Nov 15 '13 at 11:36
  • Awesome. Thanks! I've learned a ton about this so far thanks to you. To complicate things - the PHP on my server is below 5.4; but I was able to figure out the simple loop through them all (see revised code above). What I'm stuck on now is how to translate this to the original question of sending the variables to a function. I don't know how to search/display JUST a specific ID (by that node's content - not the 0, 1, 2... array) and its sibling with the added namespace syntax in there. – Cody S Nov 17 '13 at 16:07
0

First thing that popped into my mind is to use a numerical offset (which is zero-based in SimpleXML) as there is a string co-relation between the offset and the ID, the offset is always the ID minus one:

$items = $xml->item;

$id = 3;
$person = $items[$id - 1];
echo $person->id, "\n"; // prints "3"

But that would work only if - and only if - the first element would have ID 1 and then each next element the ID value one higher than it's previous sibling.

Which we could just assume by the sample XML given, however, I somewhat guess this is not the case. So the next thing that can be done is to still use the offset but this time create a map between IDs and offsets:

$items  = $xml->item;
$offset = 0;
$idMap  = [];

foreach ($items as $item) {
    $idMap[$item->id] = $offset;
    $offset++;
}

With that new $idMap map, you then can get each item based on the ID:

$id = 3;
$person = $items[$idMap[$id]];

Such a map is useful in case you know that you need that more than once, because creating the map is somewhat extra work you need to do.

So let's see if there ain't something built-in that solves the issue already. Maybe there is some code out there that shows how to find an element in simplexml with a specific attribute value?

Which leads to the point you could do it as outlined in that answer that shows how it works transparently like this:

$person = $items->attribute("id", $id);

I hope this is helpful.

Community
  • 1
  • 1
hakre
  • 193,403
  • 52
  • 435
  • 836
  • Thanks! I think I'm going to go the XPATH route - but this info is also really helpful in my overall understanding of parsing the information in a variety of ways! :) – Cody S Nov 13 '13 at 21:07