Hello guys,

I am trying to learn working with Web services using PHP. I have found many methods to access and consume these services, but I've come to know Soap Client and cURL are the best way to do it. I decided to work on an API provided by a UK Money Saving Forum 'HUKD'

I used both (SOAP & cURL) and successfully retrieved data in XML format. which is pretty neat, so far. I get the following XML as a response. (don't be afraid its just a list of data)

file.xml

(I have attached the XML and not pasted here because of its really large text of 1000 something lines and its annoying to scroll down).

Now, this is a filtered response. My response can have many 'api_item' I found that a bit challenging to work with and handle.

So this is what I did.

<?php
include_once 'api_item.php';
$api_item[] = new api_item();
$file= 'file.xml';
$xml = simplexml_load_file($file);
$api_item[0]->put_title($xml->children()->children()->children()->__toString());
?>

and it gives me this
response.txt

the response is an array of all the xml data.

so I decided to create a class and I've planned to fetch the data and get the 'api_item' data from fetched data and dump it into the class array. (its just a theory. Is it something we can do?)

this is what I designed

<?php

class api_item{

    private $title;
    private $deal_link;
    private $mobile_deal_link;
    private $deal_image;
    private $description;
    private $submit_time;
    private $hot_time;
    private $poster_name;
    private $temperature;
    private $price;
    private $timestamp;
    private $expired;
    private $deal_image_highres;

    function get_title(){
        return $title;
    }
    function get_deal_link(){
        return $deal_link;
    }
    function get_mobile_deal_link(){
        return $mobile_deal_link;
    }
    function get_deal_image(){
        return $get_deal_image;
    }
    function get_description(){
        return $description;
    }
    function get_submit_time(){
        return $submit_time;
    }
    function get_hot_time(){
        return $hot_time;
    }
    function get_poster_name(){
        return $poster_name;
    }
    function get_temperature(){
        return $temperature;
    }
    function get_price(){
        return $price;
    }
    function get_timestamp(){
        return $timestamp;
    }
    function get_expired(){
        return $expired;
    }
    function get_deal_image_highres(){
        return $deal_image_highres;
    }

    function put_title($var_title){
        $title = $var_title;
    }   
    function put_deal_link($var_deal_link){
        $deal_link = $var_deal_link;
    }
    function put_mobile_deal_link($var_mobile_deal_link){
        $mobile_deal_link = $var_mobile_deal_link; 
    }
    function put_deal_image($put_deal_image){
        $deal_image = $var_deal_image;
    }
    function put_description($var_description){
        $description = $var_description;
    }
    function put_submit_time($var_submit_time){
        $submit_time = $var_submit_time;
    }
    function put_hot_time($var_hot_time){
        $hot_time = $var_hot_time;
    }
    function put_poster_name($var_poster_name){
        $poster_name = $var_poster_name;
    }
    function put_temperature($var_temperature){
        $temperature = $var_temperature;
    }
    function put_price($var_price){
        $price = $var_price;
    }
    function put_timestamp($var_timestamp){
        $timestamp = $var_timestamp;
    }
    function put_expired($var_expired){
        $expired = $var_expired;
    }
    function put_deal_image_highres($var_deal_image_highres){
        $deal_image_highres = $var_deal_image_highres;
    }
}

?>

now how do I traverse through the result and dump my array data into the class??

weird ideas my brain gives me :S

Member Avatar
diafol
$xmlstring = file_get_contents("file.xml");
$apis = new SimpleXMLElement($xmlstring);
$json = json_encode($apis->deals);
$array = json_decode($json,TRUE);

That puts your data into an usable array ($array). If you want to make a class to deal with the data. I'm a total noob at OOP. But I'll have a look later on. :)

//EDIT

Urgh! Seems the file is a bit odd - there seem to be nested <api_item> tags. Is that right? When I tried to run a few class functions, they returned strange number of api_items (17), but when I text searched for number of "<api_item>" it gave a different number (65).

So, the question is, is this a well formed XML file?

Member Avatar
diafol

Had a little play. OOP aficionados will shoot me down here I'm sure, but here goes:

BTW, I'm keeping the data as an array, as opposed to a single 'record'. Don't know if that's what you're looking for.

<?php
$xmlstring = file_get_contents("file.xml");
$apis = new SimpleXMLElement($xmlstring);
$node = $apis->deals;
$json = json_encode($node);
$array = json_decode($json,TRUE);
$toClass = $array['api_item'];

class XMLArr{
	private $main;
	public function __construct($array){
		$this->main = (is_array($array)) ? $array : array();	
	}
	public function getRow($id){
		if(isset($this->main[$id]))return $this->main[$id];
	}
	public function getProperty($id,$field){
		if(isset($this->main[$id][$field]))return $this->main[$id][$field];	
	}
	public function getCount(){
		return count($this->main);	
	}
	public function setProperty($id,$field,$value){
		if(isset($this->main[$id][$field])){
			$this->main[$id][$field] = $value;
			return true;
		}
	}
}


$m = new XMLArr($toClass);

echo $m->getCount();

if($m->setProperty(2,'title','hello this is a new Title'))echo $m->getProperty(2,'title');

?>

** the setProperty / getProperty don't work with nested items **

Hello ardav,

thanks for your reply.

I don't think that the XML is well formed. Well, the code you've written is close to what I need.

The API also has an option to recieve data in JSON format. But I explicitly am not using it as I want to use XML so I could learn how things work.

now what I actually want to do is dump the data into dynamic array (becayse I dont't know the size of incoming data) automatically I get into a sensible array where I can get the data.

Member Avatar
diafol

Well, json was there just for a convenient 'middle man' to get where I wanted.

the $toClass array should be what you need though?

if you do:

<pre>
  <?php print_r($toClass);?>
</pre>

It should show all the api_items.

so this is what I came up with.

$xmlstring = file_get_contents("file.xml");
	$apis = new SimpleXMLElement($xmlstring);
	$node = $apis->deals;
	$json = json_encode($node);
	$array = json_decode($json,TRUE);
	$MainClass= $array['api_item']; //aray of node 'api_item'
	$new_array = array(); //new blank array
	
	foreach($MainClass as $MainClassObj) { //traverse through the array
		if(is_array($MainClassObj)) { //if it has children which itself is an array
			foreach($MainClassObj as $MainClassChild) { //traverse through the child array
				if(is_array($MainClassChild)){ //if the child has array too
					foreach($MainClassObj as $MainClassChildObj) {//traverse through the grand child array
						print_r($MainClassChildObj); // dump the data
					}
				}
			}
		}

	}

This works in some sort of way, but I think I'm just looping the print_r for 'n' number of array items in the variable.
the more I think about it, the more it hurts my brain.

i'm stuck again :(

Member Avatar
diafol

You need may a recursive function here. I'm not sure what you need as you already have all the data in a nested array (the way it should be).

Do you need to create some sort of tree? What do you intent doing with the data?

If you have a template/placeholders for html output, you will probably need to know all the keys before hand so that you can extract the data directly.

Looking at the first api_item:

<api_item> 
	<title> The Verve Urban Hymns CD Preowned £1 at pound land! </title>
	<deal_link> http://www.hotukdeals.com/deals/verve-urban-hymns-cd-preowned-1-pound-land-1143545?aui=487 </deal_link>
	<mobile_deal_link> http://m.hotukdeals.com/deals/verve-urban-hymns-cd-preowned-1-pound-land-1143545?aui=487 </mobile_deal_link>
	<deal_image> http://www.hotukdeals.com/images/threads/1143545_1.jpg </deal_image>
	<description> Only just remembered to post this! Was in Birmingham yesterday and got the Verve Urban Hymnes for £1! Bitter sweet symphony is such an awesome track! It was the store by new street. It said pre owned on it, but mine looks brand new. Perhaps the disc has been resurfaced? For a pound I don't care! </description>
	<submit_time> 14 hours, 39 minutes ago </submit_time>
	<hot_time> 11 minutes ago </hot_time>
	<poster_name> thearbiter65 </poster_name>
	<temperature> 115.489997864 </temperature>
	<price> 1 </price>
	<timestamp> 1329173213 </timestamp>
	<expired> false </expired> 
	<forum>
		<name> Deals </name>
		<url_name> deals </url_name>
	</forum>
	<category>
		<name> Entertainment </name>
		<url_name> entertainment </url_name>
	</category> 
	<merchant>
		<name> PoundLand </name>
		<url_name> poundland.co.uk </url_name>
	</merchant>
	<tags>
		<api_item>
			<name> the verve </name>
		</api_item>
		<api_item>
			<name> pound land </name>
		</api_item>
	</tags>
	<deal_image_highres> http://www.hotukdeals.com/images/threads/high-res/1143545_1.jpg </deal_image_highres>
</api_item>

WHICH GIVES

title (single, level 1)
deal_link (single, level 1)
mobile_deal_link (single, level 1)
deal_image (single, level 1)
description (single, level 1)
submit_time (single, level 1)
hot_time (single, level 1)
poster_name (single, level 1)
temperature (single, level 1)
price (single, level 1)
timestamp (single, level 1)
expired (single, level 1)
forum  (single, level 1, container)
  name (single, level 2)
  url_name (single, level 2)
category (single, level 1, container)
  name (single, level 2)
  url_name (single, level 2)
merchant (single, level 1, container)
  name (single, level 2)
  url_name (single, level 2)
tags (single, level 1, container) 
  api_item ([B]multiple[/B], level 2, container)
    name (single, level 3)

Is this the extent of it? It doesn't look too bad

I'm trying to auto-populate a tree/list based on the XML I recieve so that I can use it to display data on to a page.

All I want with that data is to display it on to my ui. sommething like this

I'm trying to auto-populate a tree/list based on the XML I recieve so that I can use it to display data on to a page.

You need may a recursive function here. I'm not sure what you need as you already have all the data in a nested array (the way it should be). . . . . . . . WHICH GIVES

title (single, level 1)
deal_link (single, level 1)
mobile_deal_link (single, level 1)
deal_image (single, level 1)
description (single, level 1)
submit_time (single, level 1)
hot_time (single, level 1)
poster_name (single, level 1)
temperature (single, level 1)
price (single, level 1)
timestamp (single, level 1)
expired (single, level 1)
forum  (single, level 1, container)
  name (single, level 2)
  url_name (single, level 2)
category (single, level 1, container)
  name (single, level 2)
  url_name (single, level 2)
merchant (single, level 1, container)
  name (single, level 2)
  url_name (single, level 2)
tags (single, level 1, container) 
  api_item (<strong>multiple</strong>, level 2, container)
    name (single, level 3)

Is this the extent of it? It doesn't look too bad

EXACTLY!

This is the reason I created my class this way:Hello guys,
.
.
.
.
.
.
.
.
.
this is what I designed

<?php

	class api_item{

		private $title;
		private $deal_link;
		private $mobile_deal_link;
		private $deal_image;
		private $description;
		private $submit_time;
		private $hot_time;
		private $poster_name;
		private $temperature;
		private $price;
		private $timestamp;
		private $expired;
		private $deal_image_highres;
		
		function get_title(){
			return $title;
		}
		function get_deal_link(){
			return $deal_link;
		}
		function get_mobile_deal_link(){
			return $mobile_deal_link;
		}
		function get_deal_image(){
			return $get_deal_image;
		}
		function get_description(){
			return $description;
		}
		function get_submit_time(){
			return $submit_time;
		}
		function get_hot_time(){
			return $hot_time;
		}
		function get_poster_name(){
			return $poster_name;
		}
		function get_temperature(){
			return $temperature;
		}
		function get_price(){
			return $price;
		}
		function get_timestamp(){
			return $timestamp;
		}
		function get_expired(){
			return $expired;
		}
		function get_deal_image_highres(){
			return $deal_image_highres;
		}
		
		function put_title($var_title){
			$title = $var_title;
		}	
		function put_deal_link($var_deal_link){
			$deal_link = $var_deal_link;
		}
		function put_mobile_deal_link($var_mobile_deal_link){
			$mobile_deal_link = $var_mobile_deal_link; 
		}
		function put_deal_image($put_deal_image){
			$deal_image = $var_deal_image;
		}
		function put_description($var_description){
			$description = $var_description;
		}
		function put_submit_time($var_submit_time){
			$submit_time = $var_submit_time;
		}
		function put_hot_time($var_hot_time){
			$hot_time = $var_hot_time;
		}
		function put_poster_name($var_poster_name){
			$poster_name = $var_poster_name;
		}
		function put_temperature($var_temperature){
			$temperature = $var_temperature;
		}
		function put_price($var_price){
			$price = $var_price;
		}
		function put_timestamp($var_timestamp){
			$timestamp = $var_timestamp;
		}
		function put_expired($var_expired){
			$expired = $var_expired;
		}
		function put_deal_image_highres($var_deal_image_highres){
			$deal_image_highres = $var_deal_image_highres;
		}
	}
?>


now how do I traverse through the result and dump my array data into the class??

weird ideas my brain gives me :S

All I want with that data is to display it on to my ui. sommething like this

Member Avatar
diafol

OK, now I get you. Once you have the array, it should be straightforward if the data is consistent. So you need the following to be extracted from the array:

title
description
temperature
deal_image
deal_image_highres

(all level 1)

then just the tags -> api_item -> name (level 3)

I'll have a look - I'll come back to you.

Member Avatar
diafol

OK, used some CSS too (as you can see I was bored!) ;)

<?php
$xmlstring = file_get_contents("trial.xml");
$apis = new SimpleXMLElement($xmlstring);
$node = $apis->deals;
$json = json_encode($node);
$array = json_decode($json,TRUE);
$toClass = $array['api_item'];
 
class XMLArr{
	private $main;
	
	public function __construct($array){
		$this->main = (is_array($array)) ? $array : array();	
	}
		
	public function getCount(){
		return count($this->main);	
	}
	
	private function getProperty($id,$field,$t=NULL){
		if(isset($this->main[$id][$field])){
			return ($t) ? mb_strimwidth($this->main[$id][$field], 0, $t + 3, "...") : $this->main[$id][$field];	
		}
			
	}
	
	private function getTags($id){
		if(isset($this->main[$id]['tags']['api_item'])){
			foreach($this->main[$id]['tags']['api_item'] as $item){
				$keeper[] = trim($item['name']);	
			}
			return implode(", ",$keeper);
		}
	}
	
	public function makeBox($id,$truncateTitle=NULL,$truncateDescription=NULL){
		$output = "<div class=\"box\">";
		$output .= "<div class=\"titlebanner\">" . $this->getProperty($id,'title',$truncateTitle) . "<span class=\"temp\">" . $this->getProperty($id,'temperature') . "</span></div>";
		$output .= "<div class=\"picbox\"><a href=\"" . $this->getProperty($id,'deal_image_highres') . "\" target=\"_blank\"><img width=\"50\" height=\"50\" src=\"". $this->getProperty($id,'deal_image') . "\" /></a></div>";
		$output .= "<div class=\"desc\">" . $this->getProperty($id,'description',$truncateDescription); 
		$output .= "<div class=\"tags\">" . $this->getTags($id) . "</div>";
		$output .= "</div></div>";
		return $output;	
	}
	
	
}
?>

<?php
header ('Content-type: text/html; charset=utf-8');
$m = new XMLArr($toClass);
$output = ""; 
for($x=0;$x<$m->getCount();$x++){ 
  $output .= $m->makeBox($x,20,100);
}
?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Untitled Document</title>
<style>
	.box{
		border: black 2px solid;
		background-color:#CCCCCC;
		color: black;	
		margin: 20px;
		width: 500px;
		padding: 20px;
		-moz-box-shadow: 10px 10px 5px #888;
		-webkit-box-shadow: 10px 10px 5px #888;
		box-shadow: 10px 10px 5px #888;
	}
	.picbox{
		border: black 2px solid;
		width: 60px;
		height: 60px;
		position:relative;
		top: -50px;
		left: 440px;
		background-color: white; 
		text-align: center;
		padding-top: 5px; 	
	}
	
	.titlebanner{
		background-color:#666666;
		color: white;
		font-family:Verdana, Geneva, sans-serif;
		font-size: 20px;	
		padding: 0 10px;
	}
	.temp{
		display:block;
		float: right;
		margin-right: 60px;	
	}
	.tags{
		border: black 2px solid;
		background-color: white;
		color: #999;
		font-weight:bold;
		font-size:12px;	
	}
</style>
</head>
<?php
echo $output;
?>

<body>
</body>
</html>


The multiple php sections are just there to show that they could (should) be in different files. Don't laugh at the OOP guys :(

You could wrap it all up so that one function does the whole lot - with no need for client loop. I added the truncate parameters as an after thought. I noticed that the title was really big in some entries. Anyway, easy enough to take off.

Chrome gives me this:

Member Avatar
diafol

Was fiddling with it earlier:

Noticed that some items didn't have tags, but a blank box still appeared. Fixed. Added tooltip for title as title could be truncated.

Had a spare half hour before picking up the son, so had a bigger fiddle with the css.

Here's the changed file:
trial_open.txt

hmm.. sorry for being silent for too long. I think I've deviated from my track here.

indeed your solution is marvellous. lets forget 'getTags' for a moment and lets assume that my xml has like around more than 15000 elements, then wouldn't it be time consuming for the script to render the details on to the screen?

I have thought of a similar solution earlier, and the same thought occurred to me. That's the main reason which made me think of alternative solution to dump the data into a temporary 'db' and accessing it later at my own leisure.

also can the same result be achieved without json?

This is as far as I could get onto the code

process.php

<?php
	if(isset($_POST['submit'])) {
		include_once 'CurlREST.php';
		$category = urlencode($_POST['category']);
		$display = urlencode($_POST['display']);
		$sort = urlencode($_POST['sort']);
		$key = "12345678932156487"; // the key has been changed here
		
		$request = array();
		$request['$category']=$category;
		$request['$display']=$display;
		$request['$sort']=$sort;
		$request['$key']=$key;
		$myClient = new CurlREST();
		$response  = $myClient->getResponse($request); // this is the array I get

		if($response) {

			$doc = new DOMDocument();
			$doc->loadXML($response);
			
			$api = $doc->getElementsByTagName("api_item");

			foreach ($api as $item){
				
				$title = $item->getElementsByTagName("title");
				$deal_link = $item->getElementsByTagName("deal_link");
				$mobile_deal_link = $item->getElementsByTagName("mobile_deal_link");
				$deal_image = $item->getElementsByTagName("deal_image");
				$description = $item->getElementsByTagName("description");
				$submit_time = $item->getElementsByTagName("submit_time");
				$hot_time = $item->getElementsByTagName("hot_time");
				$poster_name = $item->getElementsByTagName("poster_name");
				$temperature = $item->getElementsByTagName("temperature");
				$price = $item->getElementsByTagName("price");
				$timestamp = $item->getElementsByTagName("timestamp");
				$expired = $item->getElementsByTagName("expired");
				$deal_image_highres = $item->getElementsByTagName("deal_image_highres");
				
				if(isset($deal_image_highres->item(0)->nodeValue))
				{
					$time = date("m-d-Y, g:i a",$timestamp->item(0)->nodeValue);
					echo "<table><tr><td><img src=\"".$deal_image->item(0)->nodeValue."\"/></td><td><a href=\"".$deal_link->item(0)->nodeValue."\">".iconv('UTF-8', 'ASCII//TRANSLIT',$title->item(0)->nodeValue)."</a></td><td>Posted by ".$poster_name->item(0)->nodeValue." (".$time.").</td></tr></table><br /><br />";
				}
				else
				{
					echo "Element Not found!<br />";
				}
			}
		}
		else {
			echo "Bad Selection";			
		}
	}
	else {
		header("HTTP/1.1 403 Forbidden");
	}
?>

CurlREST.php

<?php
	Class CurlREST {
		public function getResponse(array $params) {
			
			$category = $params['$category'];
			$display = $params['$display'];
			$sort = $params['$sort'];
			$key = $params['$key'];
			$option_error  ="bad options selected";
			$invalid_error = "Invalid Request; Please try again!";
			$url= 'http://api.hotukdeals.com/rest_api/v2?key='.$key.'&forum='.$category.'&results_per_page='.$display.'&order='.$sort;
			$curl_handler = curl_init();
			curl_setopt($curl_handler, CURLOPT_URL,$url);
			curl_setopt($curl_handler, CURLOPT_RETURNTRANSFER, 1);
			curl_setopt ($curl_handler, CURLOPT_HEADER, 0);
			$response = curl_exec($curl_handler);
			$response_code = curl_getinfo($curl_handler, CURLINFO_HTTP_CODE);
			
			if($response_code == '200') {
				curl_close($curl_handler);
				return $response;
			}
			else {
				curl_close($curl_handler);
				return NULL;
			}
		}
	}
?>
Member Avatar
diafol

That's the main reason which made me think of alternative solution to dump the data into a temporary 'db' and accessing it later at my own leisure.

also can the same result be achieved without json?

You can do what you like with it. Put it in a file, put it in a DB, put it in an XML file of your own. As I mentioned before, the 'json' is a 'middle-man', it just acts as an intermediate to help format the array structure. If you think you can do without it, do so.

I think I've deviated from my track here.

Yep. You certainly have. Good luck with it.