{
"errorMessage": null,
"hotels": [{
"hotelId": 1177,
"hotelName": "Marriott",
"hotelFilters": [{
"filterName": "pool",
"message": "yes"
}]
}, {
"hotelId": 1542,
"hotelName": "Hilton",
"hotelFilters": [{
"filterName": "pool",
"message": "no"
}, {
"filterName": "spa",
"message": "yes"
}]
}
How do I traverse the array to get a table with hotelID, hotel Name and message? I'm lost in the levels of the array.
Take this as a first approach ;)
/**
* Transforms JSON Hotel data to PHP array
*
* #param $data The data as JSON String
* #return array|bool
*/
public function transformHotelDataFromJson($data)
{
$_data = json_decode($data);
if (array_key_exists('hotels', $_data) === false) {
return false;
}
$myData = [];
foreach ($_data['hotels'] as $hotel) {
$myData[] = [
'id' => $hotel['hotelId'],
'name' => $hotel['hotelName'],
'message' => $hotel['hotelFilters']['message'],
];
}
return $myData;
}
Related
I'm having a lot of trouble with serializing collection from which I've removed first element.
I have CompaniesCollection entity with Many2Many relation to Company entity.
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Company")
* #Groups({"get-by-collection-owner"})
*/
private $items;
When I fetch the object with that collection, I receive items as an array of two elements I've added in the first place. I serialize it
{
"id": 19,
"name": "collection dummy name",
"items": [
{
"id": 431,
"name": "Company 1"
},
{
"id": 435,
"name": "Company 2"
}
],
"createdAt": "2019-03-11T13:55:43+01:00",
"updatedAt": "2019-03-11T15:48:57+01:00"
},
Then I remove FIRST item:
$collection->removeItem($companyToRemove);
$em = $this->getDoctrine()->getManager();
$em->persist($collection);
$em->persist($companyToRemove);
$em->flush();
$results = $companiesCollectionRepository->getCollections($companyLoader->getCurrentCompany());
Serialize the $results and get not an array of one element, but object with key of second element from the previous array of items:
{
"id": 19,
"name": "collection dummy name",
"items": {
"1": {
"id": 435,
"name": "Company 2"
}
},
"createdAt": "2019-03-11T13:55:43+01:00",
"updatedAt": "2019-03-11T15:52:48+01:00"
},
When I reload the page and fetch this object, the collection is again one element array, not an object. Apparently Doctrine doesn't get new results from database, but returns data which has already loaded in the memory.
And serializer most likely treats this "array" as an "object", because it doesn't start with 0 for a key of first array element, but with key 1.
Is there any way to make that query again, so I get freshly generated keys, or refresh these keys?
EDIT:
Actually I've finally found simple solution for this: refresh after flush
$collection->removeItem($companyToRemove);
$em = $this->getDoctrine()->getManager();
$em->persist($collection);
$em->persist($companyToRemove);
$em->flush();
$em->refresh($collection);
I've faced the same problem. And here is another solution which seems working for me.
Custom normalizer, which will be used for collections with "holes" in they keys:
namespace App\Serializer\Normalizer;
use Doctrine\Common\Collections\Collection;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Serializer\SerializerAwareInterface;
use Symfony\Component\Serializer\SerializerAwareTrait;
class DoctrineCollectionNormalizer implements NormalizerInterface, SerializerAwareInterface
{
use SerializerAwareTrait;
public function normalize($object, $format = null, array $context = array()): array
{
/* #var $object Collection */
$values = $object->getValues();
$object->clear();
foreach ($values as $k => $value) {
$object->set($k, $value);
}
return $this->serializer->normalize($object, $format, $context);
}
public function supportsNormalization($data, $format = null): bool
{
if ($data instanceof Collection) {
$keys = $data->getKeys();
$count = count($keys);
$lastKey = (int) array_pop($keys);
return $count && $lastKey !== $count-1;
}
return false;
}
}
I am trying to create a dynamic endpoint for a API I am creating in order to include some data but only if it is required so I can use it in multiple places.
The idea is to have api.domain.com/vehicle to bring back basic vehicle information but if I did api.domain.com/vehicle?with=owners,history then the idea is to have a function which maps the owners and history to a class which will return data but only if it is required.
This is what I currently have.
public static function vehicle()
{
$with = isset($_GET['with']) ? $_GET['with'] : null;
$properties = explode(',', $with);
$result = ['vehicle' => Vehicle::data($id)];
foreach ($properties as $property) {
array_push($result, static::getPropertyResponse($property));
}
echo json_encode($result);
}
Which will then call this function.
protected static function getPropertyResponse($property)
{
$propertyMap = [
'owners' => Vehicle::owner($id),
'history' => Vehicle::history($id)
];
if (array_key_exists($property, $propertyMap)) {
return $propertyMap[$property];
}
return null;
}
However, the response I'm getting is being nested within a index, which I don't want it to be. The format I want is...
{
"vehicle": {
"make": "vehicle make"
},
"owners": {
"name": "owner name"
},
"history": {
"year": "26/01/2018"
}
}
But the format I am getting is this...
{
"vehicle": {
"make": "vehicle make"
},
"0": {
"owners": {
"name": "owner name"
}
},
"1": {
"history": {
"year": "26/01/2018"
}
}
}
How would I do this so it doesn't return with the index?
Vehicle::history($id) seems to return ['history'=>['year' => '26/01/2018']], ...etc.
foreach ($properties as $property) {
$out = static::getPropertyResponse($property) ;
$result[$property] = $out[$property] ;
}
Or your methods should returns something like ['year' => '26/01/2018'] and use :
foreach ($properties as $property) {
$result[$property] = static::getPropertyResponse($property) ;
}
I have a page that pulls information about TV shows from the TMDB as a json array with the help of the TMDB-API Wrapper. I am trying to get the page to display information on a tv shows's ['network'].
I have written a function for this that loops through the network array but I can't get it to work. Can anyone tell me what I'm doing wrong? Thanks.
This is my function that does not work.
//Get networks
public function getNetworks() {
$networks = array();
foreach($this->_data['networks'] as $data){
$networks[] = new Network($data, $this->getName());
}
return $networks;
}
Display.php
//Call the function and loop though results
echo ' <li>Networks: <ul>';
$networks = $tvShow->getNetworks();
foreach($networks as $network){
echo '<li>'. $network->getID() .'</li>';
}
This does work (written in the wrapper not me)
public function getSeasons() {
$seasons = array();
foreach($this->_data['seasons'] as $data){
$seasons[] = new Season($data, $this->getID());
}
return $seasons;
}
This does work
//Get Network
public function getNetworks() {
return $this->_data['networks'][0]['name'];
}
And then on display.php
echo '<b>'. $tvShow->getNetworks() .'</b>';
This is the json array
{
"backdrop_path": "/c8A5IYqIT1ez15sqA8wX5tCDTmF.jpg",
"created_by": [
{
"id": 1187819,
"name": "Matthew Weiner",
"profile_path": null
}
],
"episode_run_time": [
47,
45
],
"first_air_date": "2007-07-19",
"genres": [
{
"id": 18,
"name": "Drama"
}
],
"homepage": "http://www.amc.com/shows/mad-men",
"id": 1104,
"in_production": false,
"languages": [
"en"
],
"last_air_date": "2015-05-17",
"name": "Mad Men",
"networks": [
{
"id": 174,
"name": "AMC"
}
],
Tvshow Class
class TVShow{
//------------------------------------------------------------------------------
// Class Variables
//------------------------------------------------------------------------------
private $_data;
/**
* Construct Class
*
* #param array $data An array with the data of the TVShow
*/
public function __construct($data) {
$this->_data = $data;
}
I've set up a custom search action in my cakephp controller
public function search() {
$results = [];
$results['data'] = $this->Content->find('first',
array(
'conditions' => array($this->request->params['pass'][0] . ' like' => "%" . $this->request->params['pass'][1] . "%")), array('contain' => false)
);
if(count($results['data'])>0){
$results['success'] = true;
$this->set(compact('results'));
$this->set('_serialize', array('results'));
}
else{
$results['success'] = false;
}
}
The issue I'm running into is that the rest of my API formats data like this:
{
"success": true,
"data": [
{
"id": "5509be6c-9ef8-42c3-af39-2d492773a233",
"title": "test2",
},
{
"id": "5509be6c-9ef8-42c3-af39-2d492773a233",
"title": "test1"
}
]
}
but what I'm getting from cakephp right now for my search action is this:
{
"results": {
"success": true,
"data": {
"Content": {
"id": "52efcbeb-e984-4a2e-b76f-0cc34056922c",
"title": "Homeasdfasdf",
}
}
}}
How do I get rid of the extra "results" array wrapper so that my data is coming out in the same format?
Use the current() php function. In your controller make
$results = current($results);
before the set() call
I want to create a JSON tree from a flat structure - in this case an App.net Thread.
I want JSON like this
"id": "12345",
"name": "Ringo",
"data":
{
"avatar": "",
"text": "We All Live",
},
"children": [{
"id": "34567",
"name": "John",
"data":
{
"avatar": "",
"text": "In a pink submarine?",
},
"children": [{
"id": "35555",
"name": "George",
"data":
{
"avatar": "",
"text": "Don't be daft",
},
"children": []
}]
},{
"id": "98765",
"name": "Paul",
"data":
{
"avatar": "",
"text": "In a yellow submarine?",
},
"children": []
}]
So, each post can have multiple children. Each child can have children.
The JSON coming back from App.net is not threaded.
{
"id": "98765",
"parent": "12345"
"details": {
...}
},
{
"id": "34567",
"parent": "12345"
"details": {
...}
},
I've used json_decode() to get the JSON response in to an array. I can iterate through using foreach.
How do I put each post in the correct part of the multi dimensional array?
Parent
|_
|-child
|-child
| |-child
|-child
etc
I would use references, the oft forgotten hard link of PHP. Something like this:
I'm assuming you have a $posts array that you've gotten back from an App.net API call.
(untested, may not compile / run / may have errors / may be more efficient ways to do this)
// first throw everything into an associative array for easy access
$references = array();
foreach ($posts as $post) {
$id = $post['id'];
$post['children'] = array();
$references[$id] = $post;
}
// now create the tree
$tree = array();
foreach ($references as &$post) {
$id = $post['id'];
$parentId = $post['parent'];
// if it's a top level object, add it to the tree
if (!$parentId) {
$tree[] =& $references[$id];
}
// else add it to the parent
else {
$references[$parentId]['children'][] =& $post;
}
// avoid bad things by clearing the reference
unset($post);
}
// encode it
print json_encode($tree);
Just sketching out an answer here: all assuming you can hold all entries in RAM otherwise you will have to make some ordering assumptions and clear our your array when a full unit is done.
Create an array posts indexed by id holding structures with the details and an array of children.
Then iterate over your input array and for each element:
create posts[id] if it hasn't been created yet
fill in details for post[id]
if there is a parent, lookup (creating it if needed -- no idea how the ordering is) posts[parent_id] and add this structure to the children there.
At the end you can iterate over all posts and those without a parent are the roots with their children properly filled in.
I wrote a class and example script to accomplish what I think you wanted.
It converts a flat structure into a hierarchical one, and also accounts for orphan updates (ones without parent updates available).
Update.php
<?php
/**
* An App.net update.
*/
class Update extends ArrayObject
{
/**
* The update's children.
*
* #var array
*/
private $children = array();
/**
* The parent update.
*
* #var Update
*/
private $parent;
/**
* Adds a child to this update.
*
* #param Update The child update.
*/
public function addChild(self $child)
{
$child->setParent($this);
$this->children[] = $child;
}
/**
* Sets the parent update.
*
* #param Update The parent update.
*/
public function setParent(self $parent = null)
{
$this->parent = $parent;
}
/**
* Converts the update and its children to JSON.
*
* #param boolean $encode Automatically encode?
*
* #return string The JSON-encoded update.
*/
public function toJson($encode = true)
{
$data = $this->getArrayCopy();
if ($this->children) {
$data['children'] = array();
foreach ($this->children as $child) {
$data['children'][] = $child->toJSON(false);
}
}
if ($encode) {
return json_encode($data);
}
return $data;
}
}
build.php
<?php
require 'Update.php';
$updates = <<<UPDATES
[
{
"id": "12345",
"name": "Ringo",
"data": {
"avatar": "",
"text": "We All Live"
}
},
{
"id": "34567",
"parent": "12345",
"name": "John",
"data": {
"avatar": "",
"text": "In a pink submarine?"
}
},
{
"id": "98765",
"parent": "12345",
"name": "Paul",
"data": {
"avatar": "",
"text": "In a yellow submarine?"
}
}
]
UPDATES;
$original = json_decode($updates, true);
$parents = array();
$children = array();
foreach ($original as $update) {
if (empty($update['parent'])) {
$parents[$update['id']] = $parent = new Update($update);
if (isset($children[$update['id']])) {
foreach ($children[$update['id']] as $child) {
$parent->addChild($child);
}
unset($children[$update['id']]);
}
} else {
$child = new Update($update);
if (isset($parents[$update['parent']])) {
$parents[$update['parent']]->addChild($child);
} else {
if (false === isset($children[$update['parent']])) {
$children[$update['parent']] = array();
}
$children[$update['parent']][] = $child;
}
}
}
// Anything in children at this point are orphans
echo "Parents:\n";
foreach ($parents as $parent) {
echo $parent->toJson();
}
echo "\n\nOrphans:\n";
foreach ($children as $parent => $orphans) {
foreach ($orphans as $orphan) {
echo $orphan->toJson();
}
}