Modifying JSON data for API - php

I have built an API which essentially has three calls. Firstly, a call is made to retrieve a token. Next, I have a type of autocomplete feature which returns data and the coresponding table it should look up. This data is returned like so
[
{
"result": "Apples",
"table": "fruitTable"
},
{
"result": "Bread",
"table": "breadTable"
},
{
"result": "Pie",
"table": "pieTable"
}
]
My final route is like the following
Route::get('returnSelectedResult/{table}/{selected}', array('uses'=>'APIController#returnSelectedResult'));
Whereby it uses the lookup results above to get the right table and the selected option. Then I do the following
public function returnSelectedResult($table, $selected)
{
$tableData = DB::table($table)->where('search_item', $selected)->get();
return response($tableData, 200);
}
So this all works fine. However, the data in the table is flat. So the final thing returned to the user of the API is something like this
[
{
"search_item": "Bread",
"num_types": 34,
"most_ordered": 'Baggette',
"most_popular_day": 'Saturday',
"average_profit": 3.5,
}
]
What I am showing is a very cut down version, there is a lot more data returned but it is all at the same level. What I need to return to the user is something more along the
lines of this
[
{
"searched" : {
"search_item": "Bread"
},
"types" : {
"num_types": 34
},
"analytics" : {
"most_ordered": 'Baggette',
"most_popular_day": 'Saturday',
"average_profit": 3.5
}
}
]
What would be the best way to achieve something like this?
Thanks

You can explicitly build the array with the query results.

if you really want to build api you should use transformers for a better presentation of data .
you can use phpleague/fractal or if you want full api package i recommend you to try dingo/api .

Do something like this,
Now I have done for analytics
class MainClass
{
public $searched;
public $types;
public $analytics;
}
class AnalyticsClass
{
public $most_ordered;
public $most_popular_day;
public $average_profit;
}
$obj_data = new AnalyticsClass();
$obj_data->most_ordered = 'Baggette';
$obj_data->most_popular_day = 'Saturday';
$obj_data->average_profit = 3.5;
$obj_main = new MainClass();
$obj_main->analytics = $obj_data;

Related

Create an object / model out of JSON data, that is not an exact copy of the wanted model-object

How would one in an OOP-way "hydrate", "map", "create" (or whatever it's called) an object or model representation of this JSON data. The JSON string I've included below is the raw data received from Google's AdWords API and is not an exact copy of the structure I want and need, and I may therefor not just cast it to an object and similar.
I've been looking at various mapping libraries and hydration libraries but I cannot seem to wrap my head around how to get it to work properly.
{
"report-name": {
"#name": "Criteria performance report #5b3e67189ac85",
},
"date-range": {
"#date": "Jun 5, 2018-Jul 4, 2018",
},
"table": {
"columns": {
"column": [
{
"#name": "adGroup",
"#display": "Ad group",
},
{
"#name": "impressions",
"#display": "Impressions",
},
{
"#name": "clicks",
"#display": "Clicks",
},
{
"#name": "cost",
"#display": "Cost",
}
]
},
"row": [
{
"#adGroup": "Ad Group 1 of Another Campaign",
"#impressions": 0,
"#clicks": 0,
"#cost": 0,
},
{
"#adGroup": "Ad group 1 of Initial Campaign",
"#impressions": 0,
"#clicks": 0,
"#cost": 0,
}
]
}
}
I would imagine the object or model looking kind of like this.
There are different types of reports, but the structure look exactly the same, just the items are not containing the same attributes. The posted JSON structure represents an "AdGroup", meaning the "rows" would need to get de-serialized into different models. Basically calling "getRowItems" would need to return AdGroup-object's for the posted JSON, but may need to return other types of objects for different types of report data.
class ReportModelExample {
private $reportName;
private $reportDate;
private $reportItems;
public function getReportName()
{
return $this->reportName;
}
public function setReportName($reportName): void
{
$this->reportName = $reportName;
}
public function getReportDate()
{
return $this->reportDate;
}
public function setReportDate($reportDate): void
{
$this->reportDate = $reportDate;
}
public function getReportItems()
{
return $this->reportItems;
}
public function setReportItems($reportItems): void
{
$this->reportItems = $reportItems;
}
}

laravel - How to concatenate URL and retrieve images from database in json format

I am trying to display json array of the images stored in database to come with the full URL in Laravel. I am using CONCAT() function to concatenate the full URL of the image, but I'm getting a false URL with many dashes inside.
This is a problem in the coming output:
{
"posts": [{
"imageurl": "http:\/\/localhost:8000\/images\/1509695371.jpg"
}, {
"imageurl": "http:\/\/localhost:8000\/images\/1509695156.jpg"
}, {
"imageurl": "http:\/\/localhost:8000\/images\/1509696465.jpg"
}, {
"imageurl": "http:\/\/localhost:8000\/images\/1509697249.jpg"
}]
}
And this is the function in my controller to retrieve the images stored in database from Post table:
public function index()
{
$posts = Post::select(array(DB::raw("CONCAT('http://localhost:8000/images/', image) AS imageurl")))->get();
return response()->json(['posts' => $posts]);
}
Any help will be more appreciated!
Thank you so much #Sari It's now working fine. After changing the codes to this,
public function index()
{
$posts = json_encode(Post::select(array(DB::raw("CONCAT('http://localhost:8000/images/', image) AS imageurl")))->get(), JSON_UNESCAPED_SLASHES);
return $posts;
}
instead of doing it at the db level you could do the following in your Post model
public function getImageUrlAttribute($value)
{
return 'http://localhost:8000/'.$this->image;
}
and access it like $post->image_url

Is it possible to add non-persistent attributes to Laravel models?

I'm creating an API with Laravel (Lumen) in which there are objects which contain a field which is a path to a file.
These paths are stored as relative paths in the database but upon returning them to user I have to convert them to absolute urls.
Now I was wondering if there is a convenient way to add a non-persistent field to the model objects. Obviously there are Mutators but they are persisted to the database.
I also have thought of creating an after-middleware which traverses the object tree and converts every path field it finds but this is not an elegant way.
Here is the final transformation I need:
[
{
"id": 1,
"title": "Some title",
"media": [
{
"id": 435,
"path": "relative/path/to/some/file.ext"
},
{
"id": 436,
"path": "relative/path/to/some/file2.ext"
}
]
}
]
To:
[
{
"id": 1,
"title": "Some title",
"media": [
{
"id": 435,
"url": "http://example.com/relative/path/to/some/file.ext"
},
{
"id": 436,
"url": "http://example.com/relative/path/to/some/file2.ext"
}
]
}
]
Any idea is welcome.
You can use Laravel accessors,
From the Docs:
The original value of the column is passed to the accessor, allowing
you to manipulate and return the value.
These are not persisted in the DB but are modified as and when you access them.
For example:
class User extends Model
{
/**
* Get the user's first name.
*
* #param string $value
* #return string
*/
public function getFirstNameAttribute($value)
{
return ucfirst($value);
}
}
Usage:
$user = App\User::find(1);
$firstName = $user->first_name;
In your use case:
Define an accessor in the Media Model for path attribute.
public function getPathAttribute($value)
{
return storage_path($value);
}
If you need to access the property with a different name (alias):
public function getAliasAttribute()
{
return storage_path($this->attributes['path']);
}
// $model->alias
As #Sapnesh Naik said what you need is a simple accessor like this:
public function getPathAttribute($value)
{
$url = config('relative_url') or env('PATH') or $sthElse;
return $url.$this->attributes['path'];
}

Changing how returned json response looks

I'm trying to return a Json array and it works, but the other application I'm using the API for can't accept the format that the json is printed it (the way it looks, that is).
Example:
{
"123": [
{
"id": 1
}
]
}
But I need it to be:
"123":
{
"id": 1
}
Using this code:
$param = 123;
$array = User::all();
return \Response::json([$param => $array], 200, array(), JSON_PRETTY_PRINT);
Is this possible to do somehow?
I guess what you want is:
{
"123":
{
"id": 1
}
}
If you are sure you want to send just a single user, and not an array of users you can do:
$param = 123;
$user = User::first(); //Or any other Eloquent query, which gets the exact user you want
return response()->json([$param => $user]);

PHP Fractal transformer return embedded data

I'm trying to use Fractal for transforming API data output. This works for single items and collections, but I can't seem to get it working with embedded data. Unfortunately, I can't find lots of "how to's" on Fractal. I followed the info on the Fractal site, but it won't work. I'm using Laravel 4 as my framework.
This is what I have on my Transformer class:
protected $availableEmbeds = array(
'requirements'
);
public function transform(){ etc... }
public function embedRequirements(Regions $regions)
{
return $this->collection($regions->requirements, new RequirementsTransformer);
}
Than, inside my controller I have
$regions = Regions::with($this->eagerLoad)->get();
This gives me the result I want.
But when I pass this data to the transformer it doesn't provide the desired result:
return $this->respondWithCollection($regions, new RegionTransformer());
RespondWithCollection method
protected function respondWithCollection($collection, $callback)
{
$resource = new Collection($collection, $callback);
$fractalManager = new Manager();
$rootScope = $fractalManager->createData($resource);
//$rootScope = $this->fractal->createData($resource);
return $this->respondWithArray($rootScope->toArray());
}
This is the output:
{
"data": [
{
"id": 36218,
"name": "Netherlands",
"active": true,
"created": "2014-02-28 11:17:02"
}
],
"embeds": [
"requirements"
]
}
Where I was expecting "requirements" to be part of a relations key inside the "data" key.
Does anyone knows what I'm doing wrong?
I ran into this problem just yet. The solutions was
Transformer class:
public function embedRequirements(Regions $regions)
{
$requirements = $regions->requirements()->get();
return $this->collection($requirements, new RequirementsTransformer);
}
The get() made all the difference
$regions->requirements()->get();

Categories