roybman
roybman

Reputation: 25

php xpath query to get parent node based on value in repeating child nodes

I have an XML file structured as follows:

<pictures>
    <picture>
        <title></title>
        <description></description>
        <facts>
            <date></date>
            <place>Unites States</place>
        </facts>
        <people>
            <person>John</person>
            <person>Sue</person>
        </people>
    </picture>
    <picture>
        <title></title>
        <description></description>
        <facts>
            <date></date>
            <place>Canada</place>
        </facts>
        <people>
            <person>Sue</person>
            <person>Jane</person>
        </people>
    </picture>
    <picture>
        <title></title>
        <description></description>
        <facts>
            <date></date>
            <place>Canada</place>
        </facts>
        <people>
            <person>John</person>
            <person>Joe</person>
            <person>Harry</person>
        </people>
    </picture>
<pictures>

In one case, I need to search for pictures where place="Canada". I have an XPath that does this fine, as such:

$place = "Canada";
$pics = ($pictures->xpath("//*[place='$place']"));

This pulls the entire "picture" node, so I am able to display title, description, etc. I have another need to find all pictures where person = $person. I use the same type query as above:

$person = "John";
$pics = ($pictures->xpath("//*[person='$person']"));

In this case, the query apparently knows there are 2 pictures with John, but I don't get any of the values for the other nodes. I'm guessing it has something to do with the repeating child node, but can't figure out how to restructure the XPath to pull all of the picture node for each where I have a match on person. I tried using attributes instead of values (and modified the query accordingly), but got the same result. Can anyone advise what I'm missing here?

Upvotes: 1

Views: 2581

Answers (1)

ThW
ThW

Reputation: 19482

Let's replace the variables first. That takes PHP out of the picture. The problem is just the proper XPath expression.

//*[place='Canada']

matches any element node that has a child element node place with the text content Canada.

This is the facts element node - not the picture.

Getting the pictures node is slightly different:

//picture[facts/place='Canada']

This would select ANY picture node at ANY DEPTH that matches the condition.

picture[facts/place='Canada']

Would return the same result with the provided XML, but is more specific and matches only picture element nodes that are children of the document element.

Now validating the people node is about the same:

picture[people/person="John"]

You can even combine the two conditions:

picture[facts/place="Canada" and people/person="John"]

Here is a small demo:

$element = new SimpleXMLElement($xml);

$expressions = [
  '//*[place="Canada"]',
  '//picture[facts/place="Canada"]',
  'picture[facts/place="Canada"]',
  'picture[people/person="John"]',
  'picture[facts/place="Canada" and people/person="John"]',
];

foreach ($expressions as $expression) {
  echo $expression, "\n", str_repeat('-', 60), "\n";
  foreach ($element->xpath($expression) as $index => $found) {
     echo '#', $index, "\n", $found->asXml(), "\n";
  }
  echo "\n";
}

HINT: Your using dyamic values in you XPath expressions. String literals in XPath 1.0 do not support any kind of escaping. A quote in the variable can break you expression. See this answer.

Upvotes: 3

Related Questions