0

I am trying to add an array $item to an XML file in order to then be able to read all of the items in a later time.

I have the following PHP to perform this action:

<?php
$item = array();
$item['rating'] = $_GET['rating'];
$item['comment'] = $_GET['comment'];
$item['item_id'] = $_GET['item_id'];
$item['status'] = "pending";

//Defining $xml
$xml = new SimpleXMLElement('<root/>');
array_walk_recursive($item, array($xml, 'addChild'));
$xml = $xml->asXML();

$dom = new DOMDocument;
$dom->preserveWhiteSpace = FALSE;
$dom->loadXML($xml);

//Save XML as a file
$dom->save('reviews.xml');

However, when run what I get this in my XML file:

< ?xml version="1.0"?>

Basically my array is no where to be seen. A var_dump of $item gives

array(4) { ["rating"]=> string(1) "8" ["comment"]=> string(17) "I Really Like it!" ["item_id"]=> string(1) "9" ["status"]=> string(7) "pending" }

How could I modify my code in order to have it save an array (and if there are many keep them all) in the file reviews.xml?

Also How could I make it so that later on I would be able to access the data; for instance changing the status from pending to approved?

EDIT:

Using the following code I have been able to save my item to the file:

$item = array();
$item[$_GET['rating']] = 'rating';
$item[$_GET['comment']] = 'comment';
$item[$_GET['item_id']] = 'item_id';
$item['pending'] = 'status';

$xml = new SimpleXMLElement('<root/>');
array_walk_recursive($item, array($xml, 'addChild'));


$xml->asXML('reviews.xml');

However I am still unable to append new data to the root rather than overwriting the current saved data.

6
  • Here is silly question: have you done a var_dump or otherwise verified that there are values in your $item array? Commented May 3, 2018 at 20:04
  • Error is clear. "WARNING DOMDocument::loadXML(): Empty string supplied as input". Should turn on error reporting. No where do you assign to $xml. You print it, but no $xml = ... Commented May 3, 2018 at 20:06
  • Why aren't you using dom_import_simplexml? Commented May 3, 2018 at 20:39
  • 1
    Or just $xml->asXML('reviews.xml');? Commented May 3, 2018 at 20:41
  • However I am still unable to append new data to the root rather than overwriting the current saved data. can you show how you are trying to do that? And are you creating a separate node/element for each update, or just piling them all into the root? Commented May 3, 2018 at 22:03

2 Answers 2

1

As I was saying in my comment... The code you provided errors with WARNING DOMDocument::loadXML(): Empty string supplied as input. You never assigned anything to $xml'...

Proper error reporting/logging would help spot these mistakes.

<?php
$item = array();
$item['rating'] = 'a';
$item['comment'] = 'b';
$item['item_id'] = 'c';
$item['status'] = "pending";

//Defining $xml
$xml = new SimpleXMLElement('<root/>');
array_walk_recursive($item, array($xml, 'addChild'));

//THIS IS THE LINE YOU WERE MISSING
$xml = $xml->asXML();

$dom = new DOMDocument;
$dom->preserveWhiteSpace = FALSE;
$dom->loadXML($xml);

//Save XML as a file
$dom->save('reviews.xml');

If you echoed it out...

var_dump($dom->saveHTML()); 


> string(80)
> "<root><a>rating</a><b>comment</b><c>item_id</c><pending>status</pending></root>"

Please avoid updating your existing question with additional questions.

A database would make the task easier. Using a flat file works fine though, XML, or some other format. You will need to be able to retrieve a record by item_id, at which point you modify it, then replace it. That is the gist of it.

Sign up to request clarification or add additional context in comments.

6 Comments

This change still seems to result in the reviews.xml being virtually empty
That like being kinda pregnant? Virtually empty?
I meant that no array is being stored it still only has <?xml version="1.0"?> in it
Are you looking at it in a web browser or something rather than viewing the source? XML doesn't render like HTML. I tested the code before I posted.
I got it to partially work using the Editted method, to answer you question I was reading the source file using PHPStorm
|
1

So here's an overhaul of your code, with some changes to both your approach and the scheme of your XML, based on your various comments and updates.

So first, instead of creating XML that looks like this:

<root>
    <rating>a</rating>
    <comment>b</comment>
    <item_id>c</item_id>
    <status>pending</status>
</root>

You're going to store the XML like this:

<root>
    <item id="c">
        <rating>a</rating>
        <comment>b</comment>
        <status>pending</status>
    </item>
</root>

This is based on a few of your comments:

  1. You are wanting to add to the XML file rather than overwrite the existing file content. That suggests that you want to store multiple items. This would also explain why you have a property item_id. So rather than having a mess of XML like :

    <root>
        <rating>a</rating>
        <comment>b</comment>
        <item_id>c</item_id>
        <status>pending</status>
        <rating>d</rating>
        <comment>e</comment>
        <item_id>f</item_id>
        <status>pending</status>
        <rating>g</rating>
        <comment>h</comment>
        <item_id>i</item_id>
        <status>pending</status>
    </root>
    

where it is impossible to know which item is which, you store each set of item properties on an <item> element. Since you are going to want to easily grab an item based on its item_id in order to update that item, making item_id an attribute of the <item> makes more sense than making it a child of the <item>.

  1. You want to be able to update the status. This is where having the item_id stored on the item comes in handy. If someone submits a request with an existing item_id, you can update that item, including its status element. Or you could do it whenever you need to from some other process, etc.

Here's the code I drummed up for this. Note that it currently isn't set up to look for an existing element with that item id, but that should be possible using existing SimpleXML functions/methods.

$item = array();

$item_id = "c";
$item['rating'] = 'a';
$item['comment'] = 'b';
$item['status']  = "pending";

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

//if ratings.xml not found or not valid xml, create clean XML with <root/>
if($xml === false) {
    $xml = new SimpleXMLElement('<root/>');
}

$xml_item = $xml->addChild("item");

$xml_item->addAttribute("id", $item_id);

foreach($item as $name => $value) {
    $xml_item->addChild($name, $value);
}

$xml->asXML('ratings.xml');

Notice that one of the major changes I made to your existing code is changing from using array_walk_recursive to a simple foreach. array_walk_recursive for this purpose is a short cut that causes more issues than it solves. For instance, you had to swap your key and value on the $item array, which is confusing. It also isn't necessary for what you currently are doing, since you don't have a multi-dimensional array. And even if you did, array_walk_recursive isn't the right choice to handle looping over the array recursively because it would add each array member to the root of the XML, not add sub-arrays as children of their parent entry as they show up in the actual array. Point being, it's confusing, it doesn't add any value, and using a foreach is a lot more clear on what you are actually doing.

I've also changed

$item['item_id'] = 'c';

to

$item_id = 'c';

and then added it to the item element as an attribute like:

$xml_item->addAttribute("id", $item_id);

This is consistent with the new schema I outlined earlier.

Finally, instead of passing the XML to DOMDocument, I'm just using

$xml->asXML('ratings.xml');

SimpleXML already removes any extra whitespace, so there is no need to use DOMDocument to achieve this.

Based on some of the counterintuitive parts of your original code, it looks like you may have done a decent amount of copy and pasting to get it going. Which is where most of us start, but it's a good idea to be upfront about things like "I don't understand quite what this code is doing, I just grabbed it from a script that did some of what I need." It will save us all a lot of time and grief if we're not assuming you are using the code you have because you need to or it was a conscious decision, etc, and that we have to work within the constraints of that code.

I hope this gets you off to a good start.

Update

I was messing around with it, and came up with the following for updating existing <item> if an item with id set to $item_id already exists. It's a bit clunky, but it tested and it works.

This assumes the $item_id and $item array get set as normal, as well as retrieving the exiting XML, as covered above. I'm providing the lines just before the changes for reference:

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

//if ratings.xml not found or not valid xml, create clean XML with <root/>
if($xml === false) {
    $xml = new SimpleXMLElement('<root/>');
}
//query with xpath for existing item with $item_id
$item_with_id = $xml->xpath("/root/item[@id='{$item_id}']");

// if the xpath returns a result, update that item with new values.

if(count($item_with_id) > 0) {
    $xml_item = $item_with_id[0];

    foreach($item as $name => $value) {
        $xml_item->$name = $value;
    }
} else {
// if the xpath returns no results, create new item element.    
    $xml_item = $xml->addChild("item");

    $xml_item->addAttribute("id", $item_id);

    foreach($item as $name => $value) {
        $xml_item->addChild($name, $value);
    }
}

1 Comment

We are not worthy. We are not worthy!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.