Having trouble extracting values from the Yelp API via PHP - php

Let me be up front: I'm a PHP hack. There's probably some stupid mistakes in here. Please point them out if you see them.
What I'm trying to do: I'm creating a page for a restaurant that would like their Yelp reviews displayed. I'm using the Yelp Phone API to grab the reviews for the specific business. Please view the sample response on the Yelp API documentation located here: http://www.yelp.com/developers/documentation/phone_api#sampleResponse
What I've done:
Successfully connected to the API and returned a response
echoed values from the response array in a foreach loop.
If you view the documentation, you can see there are a few levels of the response. I can easily print, echo, whatever values from the second tier, but what I'm really after is all nested in the "reviews" section of the response. I'm having trouble figuring out how to echo the values within the reviews section (eg user_name, review_excerpt etc).
My Code:
$yelpstring = file_get_contents("http://api.yelp.com/phone_search?phone=[redactedphonenumber]&ywsid=[redactedapikey]", true);
$obj = json_decode($yelpstring);
foreach($obj->businesses as $key => $business)
{
$reviews = $business->reviews;
//print_r($reviews);
echo $reviews['user_name'];
}
If I echo $reviews, I just get the word "Array". If I print_r($reviews), I get an expected list of keys and values. If I try to echo a specific value from the array(echo $reviews['user_name'], I get nothing. Any light shed on what I'm doing wrong would be greatly appreciated. I'm sure I'm missing something simple. Thank you for your time!
Edit: print_r($reviews) output:
Array ( [0] => stdClass Object ( [rating_img_url_small] => http://media4.px.yelpcdn.com/static/201012164278297776/img/ico/stars/stars_small_2.png [user_photo_url_small] => http://media2.px.yelpcdn.com/static/201012162819681786/img/gfx/blank_user_extra_small.gif [rating_img_url] => http://media4.px.yelpcdn.com/static/201012163489049252/img/ico/stars/stars_2.png [rating] => 2 [user_url] => http://www.yelp.com/user_details?userid=vZbcPrYPSMFIDIfTub5H1g [url] => http://www.yelp.com/biz/jelly-cafe-denver#hrid:u9ckRV6tKApe6Bu93M93CA [mobile_uri] => http://m.yelp.com/biz/5G2X2q9p7QFdm-LbyutltQ?srid=u9ckRV6tKApe6Bu93M93CA [text_excerpt] => I wanted to like this place. It's got the contemporary name and it's full of hipsters. The place looked clean and the style was fun and cute. I felt like... [user_photo_url] => http://media3.px.yelpcdn.com/static/201012161186834854/img/gfx/blank_user_small.gif [date] => 2011-09-07 [user_name] => boycott p. [id] => u9ckRV6tKApe6Bu93M93CA ) [1] => stdClass Object ( [rating_img_url_small] => http://media4.px.yelpcdn.com/static/201012164278297776/img/ico/stars/stars_small_2.png [user_photo_url_small] => http://media1.px.yelpcdn.com/upthumb/MWu84G5QtmBmT9GoqjT_kg/ss [rating_img_url] => http://media4.px.yelpcdn.com/static/201012163489049252/img/ico/stars/stars_2.png [rating] => 2 [user_url] => http://www.yelp.com/user_details?userid=izF2cGrmqt-u_Z2tDZ8dbg [url] => http://www.yelp.com/biz/jelly-cafe-denver#hrid:OYLeeCMgnpZkk1c9LWu97g [mobile_uri] => http://m.yelp.com/biz/5G2X2q9p7QFdm-LbyutltQ?srid=OYLeeCMgnpZkk1c9LWu97g [text_excerpt] => Food is decent and overpriced, but service is a joke. Your food will take a minimum of 20 minutes, for the basic breakfast. Then when your food does come... [user_photo_url] => http://media1.px.yelpcdn.com/upthumb/MWu84G5QtmBmT9GoqjT_kg/ms [date] => 2011-09-06 [user_name] => April H. [id] => OYLeeCMgnpZkk1c9LWu97g ) [2] => stdClass Object ( [rating_img_url_small] => http://media2.px.yelpcdn.com/static/20101216418129184/img/ico/stars/stars_small_4.png [user_photo_url_small] => http://media1.px.yelpcdn.com/upthumb/3euzdGdLZRFxImY68MSg7w/ss [rating_img_url] => http://media2.px.yelpcdn.com/static/201012164084228337/img/ico/stars/stars_4.png [rating] => 4 [user_url] => http://www.yelp.com/user_details?userid=bHR9UU4vtx2QKZD44O0E5g [url] => http://www.yelp.com/biz/jelly-cafe-denver#hrid:njvNAzfSII3PxXyUymLZ1w [mobile_uri] => http://m.yelp.com/biz/5G2X2q9p7QFdm-LbyutltQ?srid=njvNAzfSII3PxXyUymLZ1w [text_excerpt] => Stopped here for breakfast on a friday morning. We were seated immediately and had a really friendly waitress. I ordered a side order of the Chai french... [user_photo_url] => http://media1.px.yelpcdn.com/upthumb/3euzdGdLZRFxImY68MSg7w/ms [date] => 2011-09-05 [user_name] => Diane F. [id] => njvNAzfSII3PxXyUymLZ1w ) )

Based on the output of print_r you can't reference $reviews['user_name'];
Note that $reviews is an Array of objects. So to access user_name you need to use
echo $reviews[0]->user_name;
And if you have more than one item in the array, you will need a loop like
for ($i = 0; $i<count($reviews); $i++) {
echo $reviews[$i]->user_name;
}
I hope this helps.

$reviews is an array of review objects. You'll need to loop over it to get to the data you're after.

Related

How to stop array results from being merged when they share the same array key?

I am having an issue trying to build an array where the status ID is the key and ALL posts related to the statuses are sub-arrays relating to the key (status ID).
Here's the (incorrect) array I am getting with both array_merge_recursive and manually adding items to the array (array 1):
Array
(
[res_1] => Array
(
[status_name] => NEEDS REVIEW
[status_color] => 666666
[post] => Array
(
[post_title] => Add feature that allows our team to add internal notes on ideas
[post_description] => Sometimes our team needs to leave feedback that users should not see publicly. Having this feature would allow the team to collaborate better at scale.
[categories] => Admin,Communication
)
)
[res_2] => Array
(
[status_name] => PLANNED
[status_color] => aa5c9e
[post] => Array
(
[post_title] => Add support for multiple languages
[post_description] => We have customers across the globe who would appreciate this page to be localized to their language
[categories] => Integrations
)
)
[res_3] => Array
(
[status_name] => IN PROGRESS
[status_color] => 3333cc
[post] => Array
(
[post_title] => Allow users to add an image with their feature request
[post_description] => Sometimes users want something visual, having an example really helps.
[categories] => Uncategorized
)
)
[res_4] => Array
(
[status_name] => COMPLETED
[status_color] => 7ac01d
[post] => Array
(
[post_title] => Add feature that allows #mentioning in comments
[post_description] => There is no hierarchy in comments so it's hard to reply to one specific user if there is a longer thread of comments.
[categories] => Communication
)
)
)
Here's something like what I am expecting to happen (every status ID is an array with multiple posts as sub-arrays):
Array
(
[res_1] => Array
(
[status_name] => NEEDS REVIEW
[status_color] => 666666
[post] => Array
(
[post_title] => Add feature that allows our team to add internal notes on ideas
[post_description] => Sometimes our team needs to leave feedback that users should not see publicly. Having this feature would allow the team to collaborate better at scale.
[categories] => Admin,Communication
)
)
[res_2] => Array
(
[status_name] => PLANNED
[status_color] => aa5c9e
[post] => Array
(
[post_title] => Add support for multiple languages
[post_description] => We have customers across the globe who would appreciate this page to be localized to their language
[categories] => Integrations
)
)
[res_3] => Array
(
[status_name] => IN PROGRESS
[status_color] => 3333cc
[post] => Array
(
[post_title] => Allow users to add an image with their feature request
[post_description] => Sometimes users want something visual, having an example really helps.
[categories] => Uncategorized
)
)
[res_4] => Array
(
[status_name] => COMPLETED
[status_color] => 7ac01d
[post] => Array(
[0] => Array (
[post_title] => Add feature that allows #mentioning in comments
[post_description] => There is no hierarchy in comments so its hard to reply to one specific user if there is a longer thread of comments.
[categories] => Communication
)
[1] => Array (
[post_title] => Feature Number 5
[post_description] => lorum ipsum awesomeness.
[categories] => Admin
)
)
)
Here's what I've tried:
Running two separate DB queries: one to fetch statuses and another to fetch posts then merging the arrays recursively and changing the array keys to a string. This does the same thing, post 5 never shows up in the newly merged array.
Same as above - ran two separate queries and rebuilt the array manually, the same result the 5th post never appears.
I printed out the database result from $stmt2->fetchAll(); all 5 posts are there in the result-set directly from the database. The 5th one just won't persist when merging arrays or building a fresh one so the posts can relate to the statuses.
I also tried joining the tables with SQL but even grouping by resolution_id does the same thing, post number 5 gets lost by the grouping. I've tried sub-queries too.
DB Results array for just posts:
Array
(
[0] => Array
(
[title] => Feature number 5
[0] => Feature number 5
[description] => lorum ipsum awesomeness
[1] => lorum ipsum awesomeness
[resolution_id] => 4
[2] => 4
[category_names] => Admin
[3] => Admin
)
[1] => Array
(
[title] => Allow users to add an image with their feature request
[0] => Allow users to add an image with their feature request
[description] => Sometimes users want something visual, having an example really helps.
[1] => Sometimes users want something visual, having an example really helps.
[resolution_id] => 3
[2] => 3
[category_names] => Uncategorized
[3] => Uncategorized
)
[2] => Array
(
[title] => Add support for multiple languages
[0] => Add support for multiple languages
[description] => We have customers across the globe who would appreciate this page to be localized to their language
[1] => We have customers across the globe who would appreciate this page to be localized to their language
[resolution_id] => 2
[2] => 2
[category_names] => Integrations
[3] => Integrations
)
[3] => Array
(
[title] => Add feature that allows #mentioning in comments
[0] => Add feature that allows #mentioning in comments
[description] => There is no hierarchy in comments so it's hard to reply to one specific user if there is a longer thread of comments.
[1] => There is no hierarchy in comments so it's hard to reply to one specific user if there is a longer thread of comments.
[resolution_id] => 4
[2] => 4
[category_names] => Communication
[3] => Communication
)
[4] => Array
(
[title] => Add feature that allows our team to add internal notes on ideas
[0] => Add feature that allows our team to add internal notes on ideas
[description] => Sometimes our team needs to leave feedback that users should not see publicly. Having this feature would allow the team to collaborate better at scale.
[1] => Sometimes our team needs to leave feedback that users should not see publicly. Having this feature would allow the team to collaborate better at scale.
[resolution_id] => 1
[2] => 1
[category_names] => Admin,Communication
[3] => Admin,Communication
)
)
Since the data is always going to be dynamic (users can choose status names and create as many as they need to) I can't just hard-code the status names/ids and run 4 queries to populate the columns.
To prevent this from being an essay long post, here are the bits of code that are building the array from array 1:
Builds the initial statuses array from the query results from the resolutions table.
$statuses = [];
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
$statuses['res_' . $row['id']] = ['status_name' => $row['name'], 'status_color' => $row['color']];
}
Adds the individual posts to the statuses array:
foreach ($dbposts as $row2) {
$statuses['res_' . $row2['resolution_id']]['post'] = ['post_title' => $row2['title'], 'post_description' => $row2['description'], 'categories' => $row2['category_names']];
}
The resolution ID is concatenated with res_ from when I tried doing an array merge based on keys. It would not merge when the keys were just integers.
Finally some context behind why I am trying to do what I am trying to do. I am building a platform where companies can have users submit feature requests and view the results in a list view or board view. The list view was a piece of cake since the board view needs to be per status, this is where I am having trouble. I hard-coded the board view values to demonstrate the expected end-result:
Not looking for someone to write my code for me, just looking for some guidance - perhaps I am building or merging the arrays wrong?
To build the array of posts, you need to append elements to the array of posts. Currently, you are just assigning a single element to the array over and over, which overwrites the previous value of the entire array.
The code to append posts:
$statuses['res_' . $row2['resolution_id']]['post'][] = ['post_title' => $row2['title'], 'post_description' => $row2['description'], 'categories' => $row2['category_names']];
Note the [] which I added to the end of the left side of the assignment operator.

Get fields from array

I'm working on a facebook app to retrieve information from the user. The overall goal is to make a "interactive story" about the user. So I want the name, birthday etc. But I also want to know their favorite bands. The problem is that I can display the array with all the info needed. But that is to much. I don't want to know the category or the id, I only want to know the name.
$user_music = $facebook->api('/me/music');
print_r ($user_music);
If I use this code, I get something like this:
Array ( [data] => Array (
[0] => Array ( [category] => Musician/band [name] => Red Hot Chili Peppers [created_time] => 2011-03-21T15:01:57+0000 [id] => 8335563918 )
[1] => Array ( [category] => Musician/band [name] => Train [created_time] => 2011-03-21T15:01:57+0000 [id] => 15313895735 ) )
I only want the bands name, but if I use this code:
$user_music = $facebook->api('/me/music');
$music_name = $user_music["name"];
echo $music_name;
it says it doesn't know "name".
You missed 1 array level. To access "name" you should use:
$user_music["data"][0]["name"]
or to get all names you should iterate
foreach($user_music["data"] as $data) {
echo $data["name"];
}
you should do something like this:
$user_music['data'][0]["name"]
or run $user_music['data'] in a foreach loop

php multi dimension array?

I am retrieving a multidimensional php array (I think) from an API, now all of the values return perfectly and when I dump the array with print_r I get this:
Event Object
(
[title] => test
[link] => google.com
[updated] => 2013-03-06T12:08:56.521-05:00
[id] => test
[name] => Copy of Copy of Copy of Mar 05, 2013 - TEST4
[description] =>
[registered] => 2
[createdDate] => 2013-03-06T12:08:56.521-05:00
[status] => COMPLETE
[eventType] => OTHER
[eventLocation] => EventLocation Object
(
[location] => test
[addr1] => test
[addr2] =>
[addr3] =>
[city] => madrid
[state] => andalucia
[country] =>
[postalCode] => 06103
)
[registrationUrl] => https://google.com
[startDate] => 2013-03-07T13:00:00-05:00
[endDate] => 2013-03-07T13:00:00-05:00
[publishDate] => 2013-03-06T12:11:15.958-05:00
[attendedCount] => 0
[cancelledCount] => 0
[eventFeeRequired] => FALSE
[currencyType] => USD
[paymentOptions] => Array
(
)
[registrationTypes] => Array
(
[0] => RegistrationType Object
(
[name] =>
[registrationLimit] =>
[registrationClosedManually] =>
[guestLimit] =>
[ticketing] =>
[eventFees] => Array
(
)
)
)
)
Now bumbling through wit my basic PHP i have found that i can list all of the first array items from [title] to [eventType] like this:
<?php
// get details for the first event returned
$Event = $ConstantContact->getEventDetails($events['events'][0]);
reset($Event);
while (list($key, $value) = each($Event)) {
echo "$key => $value \r\n<br/>";
}
?>
my question: All I need to do it retrieve [title] and [startDate] I don't need the rest now I could just hide the rest using Js and css but i am sure i am just being an idiot and there is an easier way to traverse this array so it only spits out the two values i need.
How do i do this?
You do not have to traverse the whole object. Just access the properties you want:
$title = $Event->title;
$startDate = $Event->startDate;
// or
echo $Event->title;
echo $Event->startDate;
It's actually an object - not an (associative) array!
What's the difference?
An object is an instance of a class. A class has methods and attributes (member variables).
Unlike C++ or some other OOP languages, you can define attributes dynamically without declaring them in the class declaration.
An array is simply a container for keys and their values.
It seems that it's not an array but an object so something like this:
echo $Event->title;
echo $Event->startDate;
Is it ...
<?php
// get details for the first event returned
$Event = $ConstantContact->getEventDetails($events['events'][0]);
reset($Event);
echo $Event->$title . "<br/>";
echo $Event->$startDate . "<br/>";
?>
? Or am I too simple?

looping through an object twice - php

I'm playing around with a zencart trying to make it do what I want but I've run out of ideas of what to Google.
When I query the DB using the zencart function the software returns an object which looks like:
queryFactoryResult Object
(
[is_cached] =>
[resource] => Resource id #117
[cursor] => 11
[EOF] =>
[fields] => Array
(
[products_id] => 5582
[products_description] => description here
[products_name] => Lucky magnet – Each petal...
[products_type] => 1
[products_quantity] => 0
[products_image] => EachPetalMag.jpg
[products_price] => 3.4000
[products_status] => 1
[products_ordered] => 14
[master_categories_id] => 21
[supplier_id] => 7
)
)
I have to loop through once to count how many master_categories are there before I can do anything else:
while (!$products->EOF) {
$products_count++;
$supcats[$products->fields['master_categories_id']] = $products->fields['master_categories_id'];
$products->MoveNext();
}
I then need to loop through the object again using the while loop like above, I've tried:
reset($products);
and
$products->EOF = FALSE;
but they don't work. Is there a way to do this with out having to send the query again?
Regular array-like operations on Zen Cart queryFactoryResult won't work as it's not an array, so this won't work:
reset($products);
To loop through the variable again use queryFactoryResult->Move(row_number) method:
$products->Move(0);

What's the best way to process this XML feed?

I've got an XML document that gives me addresses. Here's an excerpt:
<ezi:orderaddresses>
<ezi:orderaddress>
<ezi:addresstype>billing</ezi:addresstype>
<ezi:name>Jason Fonseca</ezi:name>
<ezi:companyname>Cyber</ezi:companyname>
<ezi:address1>8 springstein</ezi:address1>
<ezi:division>NT</ezi:division>
<ezi:postalcode>34245</ezi:postalcode>
<ezi:countrycode>AU</ezi:countrycode>
<ezi:email>jason#bigcreative.com.au</ezi:email>
<ezi:phone>89549854</ezi:phone>
<ezi:mobilephone>984590598</ezi:mobilephone>
</ezi:orderaddress>
<ezi:orderaddress>
<ezi:addresstype>shipping</ezi:addresstype>
<ezi:name>Jason Fonseca</ezi:name>
<ezi:companyname>Cyber</ezi:companyname>
<ezi:address1>8 springstein</ezi:address1>
<ezi:division>NT</ezi:division>
<ezi:postalcode>34245</ezi:postalcode>
<ezi:countrycode>AU</ezi:countrycode>
<ezi:email>jason#bigcreative.com.au</ezi:email>
<ezi:phone>89549854</ezi:phone>
</ezi:orderaddress>
</ezi:orderaddresses>
In addition to the above format of two "orderaddress" tags, there can also be one, for example:
<ezi:orderaddresses>
<ezi:orderaddress>
<ezi:addresstype>billing</ezi:addresstype>
<ezi:name>Jason Fonseca</ezi:name>
<ezi:companyname>Cyber</ezi:companyname>
<ezi:address1>8 springstein</ezi:address1>
<ezi:division>NT</ezi:division>
<ezi:postalcode>34245</ezi:postalcode>
<ezi:countrycode>AU</ezi:countrycode>
<ezi:email>jason#bigcreative.com.au</ezi:email>
<ezi:phone>89549854</ezi:phone>
<ezi:mobilephone>984590598</ezi:mobilephone>
</ezi:orderaddress>
</ezi:orderaddresses>
What I find is that when using simple xml to interpret this, in the first instance, I get the following:
[orderaddresses] => SimpleXMLElement Object
(
[orderaddress] => Array
(
[0] => SimpleXMLElement Object
(
[addresstype] => billing
[name] => Jason Fonseca
[companyname] => Cyber
[address1] => 8 springstein
[division] => NT
[postalcode] => 34245
[countrycode] => AU
[email] => jason#bigcreative.com.au
[phone] => 89549854
[mobilephone] => 984590598
)
[1] => SimpleXMLElement Object
(
[addresstype] => shipping
[name] => Jason Fonseca
[companyname] => Cyber
[address1] => 8 springstein
[division] => NT
[postalcode] => 34245
[countrycode] => AU
[email] => jason#bigcreative.com.au
[phone] => 89549854
)
)
)
And in the second instance, I get this:
[orderaddresses] => SimpleXMLElement Object
(
[orderaddress] => SimpleXMLElement Object
(
[addresstype] => billing
[name] => Jason Fonseca
[companyname] => Cyber
[address1] => 8 springstein
[division] => NT
[postalcode] => 34245
[countrycode] => AU
[email] => jason#bigcreative.com.au
[phone] => 89549854
[mobilephone] => 984590598
)
)
The Keen observer will notice that now if I try to access
orderaddresses->orderaddress that this would have a different structure depending on whether there are two addresses (or more) or just one address.
Example 2 it is a numerically index array, example two it is a an associative object.
To standardise this, I've used some code that looks like this :
if(!isset($content['orderlines']['orderline'][0]))
{
$temp = $content['orderlines']['orderline'];
unset($content['orderlines']['orderline']);
$content['orderlines']['orderline'][0] =$temp;
}
(you can ignore the fact that i'm using associative arrays here, i've got a routine that performs a conversion but I've checked and this routine does not change the result).
My question is, how am I supposed to correctly inteperet this data? Having that slice of code everytime I try to access the orderaddress and order lines is really messy. Isn't there a better way?
The thought of editing my conversion routine to handle this has crossed my mind. If you think that is the way to go, then let me know, but I thought to myself surely this problem must come up a lot and someone must have a smart, and brief solution to it?
Thanks
According to the documentation for simplexml you should always be able to iterate over and subscript properties even if there is only one of them, this may be the 'best' way of achieving what you want. e.g. :
for($xml->thingThereMayBeOneOrMoreOf as $item) {
//handle $item
}
or, if you only need to handle the first item
$item=$xml->thingThereMayBeOneOrMoreOf[0];
//handle $item
Also, according to the documentation these properties are not in fact arrays, but accessible (subscriptable) and iterable properties, just that var_dump etc will convert them to / treat them as arrays for the purposes of dumping.
I haven't actually tested this, ymmv.

Categories