How to select attributes in XML based on an ID? - php

I am trying to get a group of attributes from an XML file based on the ID. I have looked around and haven't found a solution that works.
Here is my XML:
<data>
<vico>2</vico>
<vis>
<vi>
<id>1</id>
<name>Vill1</name>
<att>2</att>
<hp>100</hp>
<xp>10</xp>
</vi>
<vi>
<id>2</id>
<name>Vill2</name>
<att>3</att>
<hp>120</hp>
<xp>12</xp>
</vi>
</vis>
</data>
What I am looking to do is create a script that takes the value of vico and does mt_rand(0,vico) (Already created this part of the code), and based on the number that comes up, it pulls that node.
For instance, if the number is 2, I would like it to pull all of the attributes where the id in the xml file is 2. I am thinking I have to make the ID a parent of the other attributes, but I am not sure. For the life of me I can't figure this out. I also want to make sure this is even possible before I invest anymore time.
I do realize this can be done very easily with mySQL but I have chose not to do it that way for portability as I am working off a thumb drive. Any help would be greatly appreciated.
Working code for pulling based on ID:
$xml = simplexml_load_file("vi.xml")
or die("Error: Cannot create object");
$vc = $xml->vico;
$select = mt_rand()&$vc;
if ($select == 0) {
$select = $select + 1;
}
$result = $xml->xpath("/data/vis/vi/id[.='".$select."']/parent::*");
if ($result) {
$node = $result[0];
$name = $node->name;
$att = $node->att;
$hp = $node->hp;
$xp = $node->xp ;
} else {
// nothing found for this id
}

Using xpath to retrieve the node where id=your value (it is based on the value).
$result = $xml->xpath("/data/vis/vi/id[.='".$select."']/parent::*");
if ($result) {
$node = $result[0];
$name = $node->name;
$att = $node->att;
$hp = $node->hp;
$xp = $node->xp ;
} else {
// nothing found for this id
}

If you're using SimpleXML:
$name = $xml->vis->vi[$vico]->name;
$att = $xml->vis->vi[$vico]->att;
$hp = $xml->vis->vi[$vico]->hp;
$xp = $xml->vis->vi[$vico]->xp;

Related

Is it possible to convert from .php to .xml after the processing is complete?

I have a PHP Script that connects to a database, fetches results and presents them as an XML file.
<?php
header('Content-type: text/xml');
mysql_connect('host','user','password');
mysql_select_db('a8273293_blogger');
$sql = "Select * from Messages";
$q = mysql_query($sql) or die(mysql_error());
$result = mysql_query("SELECT COUNT(id) AS total FROM Messages"); // Query to count IDs
$data = mysql_fetch_assoc($result); // Fetch query result
$countNo = $data['total']; // Store count value in var $countNo
$dom = new DOMDocument();
$root = $dom->createElement('root'); // Root element
$dom->appendChild($root);
while($r = mysql_fetch_array($q)){
$user = $dom->createElement('username'); // Define tag name
$userContent = $dom->createTextNode($r['user_username']); // Field
$user->appendChild($userContent); // Add content to tag
$text = $dom->createElement('text');
$textContent = $dom->createTextNode($r['text']);
$text->appendChild($textContent);
$posted_at = $dom->createElement('posted_at');
$posted_atContent = $dom->createTextNode($r['posted_at']);
$posted_at->appendChild($posted_atContent);
$messages = $dom->createElement('messages'); // Container
$messages->appendChild($user); // Add field
$messages->appendChild($text);
$messages->appendChild($posted_at);
// Count
$count = $dom->createElement('result'); // Define tag name
$countContent = $dom->createTextNode($countNo); // Field
$count->appendChild($countContent); // Add content to tag
$root->appendChild($count); // Add blah
$root->appendChild($messages); // Add content to root
}
$xmlString = $dom->saveXML();
echo $xmlString;
?>
This script creates an XML document which is all good but after the script is finished can i convert the file to XML? This is because I want to attach a Cron Job so that information can be gathered again after each day... This will eventually be used to feed information to an app.
Also is it better to try and concatenate or just create a new row with all the data I need in it?
For example if I had firstname, surname, title and I wanted everything from these 3 as one string in an XML tag as a pose to three could I just merge them?
Also if I only wanted my count function to output once could I place it outside of the while loop?
I appreciate this question is rather verbose but I am unsure about particular parts.
instead of echo $xmlString; add this
$myfile = fopen("output.xml", "w");
fwrite($myfile, $xmlString);
fclose($myfile);
set the name and path for output.xml based on your needs.

PHP crawling data from website

I am currently trying to crawl alot of data from a website, however I am struggling a little bit with it. It has an a-z index and 1-20 index, so it has a bunch of loops and DOM stuff in there. However, it managed to crawl and save about 10.000 rows at first run, but now I am at around 15.000 and it is only crawling around 100 per run.
It is probably because it has to skip the rows that it already has inserted, (made a check for that). I cant think of a way to easily skip some pages, as the 1-20 index varies a lot (for one letter there are 18 pages, other letter are only 2 pages).
I was checking if there already was an record with the given ID, if not, insert it. I assumed that would be slow, so now before the script stars I retrieve all rows, and then check with an in_array(), assuming thats faster. But it just wont work.
So my crawler is navigating 26 letters, 20 pages each letter, and then up to 50 times each page, so if you calculate it, its a lot.
Thought of running it letter by letter, but that wont really work as I am still stuck at "a" and cant just hop onto "b" as I will miss records from "a".
Hope I have explained the problem good enough for someone to help me. My code kinda looks like this: (I have removed some stuff here and there, guess all the important stuff is in here to give you an idea)
function in_array_r($needle, $haystack, $strict = false) {
foreach ($haystack as $item) {
if (($strict ? $item === $needle : $item == $needle) || (is_array($item) && in_array_r($needle, $item, $strict))) {
return true;
}
}
return false;
}
/* CONNECT TO DB */
mysql_connect()......
$qry = mysql_query("SELECT uid FROM tableName");
$all = array();
while ($row = mysql_fetch_array($qru)) {
$all[] = $row;
} // Retrieving all the current database rows to compare later
foreach (range("a", "z") as $key) {
for ($i = 1; $i < 20; $i++) {
$dom = new DomDocument();
$dom->loadHTMLFile("http://www.crawleddomain.com/".$i."/".$key.".htm");
$finder = new DomXPath($dom);
$classname="table-striped";
$nodes = $finder->query("//*[contains(concat(' ', normalize-space(#class), ' '), ' $classname ')]");
foreach ($nodes as $node) {
$rows = $finder->query("//a[contains(#href, '/value')]", $node);
foreach ($rows as $row) {
$url = $row->getAttribute("href");
$dom2 = new DomDocument();
$dom2->loadHTMLFile("http://www.crawleddomain.com".$url);
$finder2 = new DomXPath($dom2);
$classname2="table-striped";
$nodes2 = $finder2->query("//*[contains(concat(' ', normalize-space(#class), ' '), ' $classname2 ')]");
foreach ($nodes2 as $node2) {
$rows2 = $finder2->query("//a[contains(#href, '/loremipsum')]", $node2);
foreach ($rows2 as $row2) {
$dom3 = new DomDocument();
//
// not so important variable declarations..
//
$dom3->loadHTMLFile("http://www.crawleddomain.com".$url);
$finder3 = new DomXPath($dom3);
//2 $finder3->query() right here
$query231 = mysql_query("SELECT id FROM tableName WHERE uid='$uid'");
$result = mysql_fetch_assoc($query231);
//Doing this to get category ID from another table, to insert with this row..
$id = $result['id'];
if (!in_array_r($uid, $all)) { // if not exist
mysql_query("INSERT INTO')"); // insert the whole bunch
}
}
}
}
}
}
}
$uid is not defined, also, this query makes no sense:
mysql_query("INSERT INTO')");
You should turn on error reporting:
ini_set('display_errors',1);
error_reporting(E_ALL);
After your queries you should do an or die(mysql_error());
Also, I might as well say it, if I don't someone else will. Don't use mysql_* functions. They're deprecated and will be removed from future versions of PHP. Try PDO.

PHP / Zend / Google Spreadsheet not getting all rows

I am trying to get all the rows from a Google spreadsheet via a PHP/Zend script. This is the script I am using:
$service = Zend_Gdata_Spreadsheets::AUTH_SERVICE_NAME;
$client = Zend_Gdata_ClientLogin::getHttpClient('xxxxxxxxx', 'xxxxxxx', $service);
$spreadsheetService = new Zend_Gdata_Spreadsheets($client);
// Get spreadsheet key
$spreadsheetsKey = 'xxxxxxxxxxxxx';
$worksheetId = 'xxx';
// Get cell feed
$query = new Zend_Gdata_Spreadsheets_CellQuery();
$query->setSpreadsheetKey($spreadsheetsKey);
$query->setWorksheetId($worksheetId);
$cellFeed = $spreadsheetService->getCellFeed($query);
// Build an array of entries:
$ssRows = array();
$ssRow = array();
$titleRow = array();
$tableRow = 1;
foreach($cellFeed as $cellEntry) {
$row = $cellEntry->cell->getRow();
$col = $cellEntry->cell->getColumn();
$val = $cellEntry->cell->getText();
// Add each row as a new array:
if ($row != $tableRow) {
array_push($ssRows, $ssRow);
$ssRow = array();
// Move to the next row / new array
$tableRow = $row;
}
// Build the array of titles:
if ($row == 1) {
$titleRow[$col] = $val;
}
// Build the array of results with the title as the key:
else {
$key = $titleRow[$col];
$ssRow[$key] = $val;
}
}
// Pass the results array:
return array_reverse($ssRows);
This builds me an array with MOST of the details from the spreadsheet, however it always misses off the last entry - can anyone see what I am doing wrong, or is there a better way to get all the data from the spreadsheet?
The form is a 3 part form, based on different answers. On filling out one part, I want to display a URL back to the form, with some details from the first form pre-filled to make the second part of the form faster to fill out. This is all fine, it is simply the missing last entry that is the major problem!
Thanks!
Your code works like this:
if (next_row) {
data[] = current_row
current_row = array();
}
if (first_row) {
title_row logic
} else {
add cell to current_row
}
So you only add the rows to your collector once you go to the next row. This will miss the last row because you'll miss that last transition.
The easy fix is to add array_push($ssRows, $ssRow); right after the foreach loop. You will need to add a check for 0 rows, this should be skipped then.
Perhaps a more proper fix is to iterate by row, then by cell, rather than just by cell.

SimpleXML to get specific data from an XML file

I am making a plugin for JomSocial that will display billing information for a logged-in user, based on an XML file. I have made good headway creating the plugin, I just cant seem to get the syntax right to create php statements so I can populate data in various places on the page. Here is the XML file:
<Inquiry>
<Billing>
<Version>4.5.1</Version>
<startTime><![CDATA[4/15/2014 11:09 PM]]></startTime>
<endTime><![CDATA[4/15/2014 11:12 PM]]></endTime>
<Date>20140415</Date>
<MemberId ID="0ESING">
<BillingInfo>
<StatementEndDate>20140430</StatementEndDate>
<BillingSubAccount>
</BillingSubAccount>
<BalanceForward>628.32</BalanceForward>
<BalanceDue>372</BalanceDue>
<Payments>-300</Payments>
</MemberId>
<MemberId ID="F00421">
</BillingInfo>
<BillingInfo>
<StatementEndDate>20140430</StatementEndDate>
<BillingSubAccount>
</BillingSubAccount>
<BalanceForward>1158.36</BalanceForward>
<BalanceDue>93.45</BalanceDue>
<Payments>-1158.36</Payments>
Here is the PHP so far:
$user =& CFactory::getRequestUser();
$cuser = CFactory::getUser();
$owner = CFactory::getUser($row->user->id);
$ptype = $cuser->getProfileType();
$billingid = $owner->getInfo('FIELD_BILLINGID');
$lastname = $owner->getInfo('FIELD_FAMILYNAME');
$uname = $cuser->username;
$memid = $cuser->id;
$name = $cuser->getDisplayName();
$isMine = COwnerHelper::isMine($cuser->id, $user->id);
$config = CFactory::getConfig();
$source = file_get_contents('data/201404.xml');
$xml = new SimpleXMLElement($source);
$balance = $xml->Billing->MemberId->BillingInfo->BalanceDue;
$BalanceForward = $xml->Billing->MemberId->BillingInfo->BalanceForward;
$Payments = $xml->Billing->MemberId->BillingInfo->Payments;
ob_start();
if( $isMine ) {
if($ptype == '2') {
if(strcasecmp($uname, $billingid) == 0) {
Then, in page to call the fields:
<?php echo "<div>Balance Due: $". $balance ." | Balance Forward: $" . $BalanceForward . " | Payment: $" . $Payments . "</div>"; ?>
This pulls in the first record of the XML file. I was trying something like this for hours:
$source = file_get_contents('data/201404.xml');
$xml = new SimpleXMLElement($source);
$balance = $xml->Billing->MemberId[.$uname.]->BillingInfo->BalanceDue;
$BalanceForward = $xml->Billing->MemberId[.$uname.]->BillingInfo->BalanceForward;
$Payments = $xml->Billing->MemberId[.$uname.]->BillingInfo->Payments;
to no avail. I would like to 'pull' the child node from the XML where the MemberId ID= "yadayada" is equal to the $uname. I hope I am being clear, this is my first post on Stackoverflow!
Using the square bracket notation accesses the attribute by it's name, so you are asking for member[0ESING] which isn't right because the attribute is named ID.
You can iterate of the members to find the match like so:
foreach($xml->Billing->MemberId as $member){
if($member['ID'] == $uname){
$balance = $member->BillingInfo->BalanceDue;
$BalanceForward = $member->BillingInfo->BalanceForward;
$Payments = $member->BillingInfo->Payments;
}
}
Why are you not using simplexml_load_file() instead? It saves you the hassle in loading the file manually and putting it in a simplexml_element instead. Furthermore, I think your issues arrise because the XML file itself is invalid, it need 1 root element but instead seems to contain zero (or multiple, depends on how one would read the file).

Optimizing code to search for duplicate records in an XML file

I have an XML file containing inventory, about 20,000 items. The records have a SKU and itemid. I want to search through the XML file and find duplicate SKUs.
XML looks like this
<SKUDetails>
<SKU>AAAAA</SKU>
<Price currencyID="USD">10</Price>
<Quantity>4</Quantity>
<ItemID>11111111</ItemID>
</SKUDetails>
<SKUDetails>
<SKU>BBBBB</SKU>
<Price currencyID="USD">10</Price>
<Quantity>10</Quantity>
<ItemID>2222222</ItemID>
</SKUDetails>
I wrote the following PHP code to process this file. It takes about 500 seconds.
I go through each node in the xml file and add a record to a database for the sku and itemid. If the sku already exists in the database i add a second itemid.
Is there a way to optimize this, or is there another method to do this.
here is the code
$doc = new DOMDocument;
$doc->load('../_result/'.$_GET["file"].'.xml');
$xpath = new DOMXPath($doc);
$query = "//SKUDetails";
$SKUDetailsNodes = $xpath->query($query);
echo("Total SKUs ".$SKUDetailsNodes->length."<br />\n");
if($SKUDetailsNodes->length > 0) {
foreach ($SKUDetailsNodes as $node) {
$query = "//ItemID";
$ItemIDNodes = $xpath->query($query,$node);
$ItemID = $ItemIDNodes->item(0)->nodeValue;
$query = "//SKU";
$SKUNodes = $xpath->query($query,$node);
$SKU = $SKUNodes->item(0)->nodeValue;
if($SKU != '') {
$insert_query = "IF NOT EXISTS ".
"(SELECT * FROM dump_inventory WHERE (sku = '$SKU')) ".
"INSERT INTO dump_inventory (sku,item1) VALUES ('$SKU',$ItemID) ".
"ELSE ".
"UPDATE dump_inventory SET item2 = $ItemID WHERE sku = '$SKU'";
if(!$insert_exec = sqlsrv_query($conn,$insert_query)) {
print_r(sqlsrv_errors());
}
}
}
DITCH THE DATABASE! That is why it is taking so long. Just keep the list in memory. Even 20k items is not that big to keep in a list!
If it is a one to many relationship you probably don;t want to store it in the database the way you are. I would suggest one table of SKUs and one table of items with a foreign key reference to the SKU table.
Also:
I would probably assemble the array of items from the XML and then run one insert statement. This is likely to be much much faster.

Categories