PHP Fractal transformer return embedded data - php

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();

Related

How to show array in Json with Fractal?

I want to save REST API data with array to database, its work, all value save to the table, but i still get error when i want to view the result in json.
Here is my error
"message": "Argument 1 passed to App\\Transformers\\NewLoanOfferTransformer::transform() must be an instance of App\\Model\\AntiAttrition, array given, called in /home/insko23/testing.insko.my/vendor/league/fractal/src/Scope.php on line 338",
My Controller
public function new_loan_offer(Request $request, AntiAttrition $antiattrition)
{
$new = array_map(null, $request->mo_id, $request->id_clrt,$request->TaskID,$request->ACID,$request->CustIDNo);
foreach($new as $new) {
$request = new AntiAttrition;
$request->mo_id = $new[0];
$request->id_clrt = $new[1];
$request->TaskID = $new[2];
$request->ACID = $new[3];
$request->CustIDNo = $new[4];
$request->save();
}
$response = fractal()
->item($new)
->transformWith(new NewLoanOfferTransformer)
->toArray();
return response()->json($response,201);
}
My App\Transformer
<?php
namespace App\Transformers;
use App\Model\AntiAttrition;
use App\Model\SettlementInfo;
use League\Fractal\TransformerAbstract;
class NewLoanOfferTransformer extends TransformerAbstract
{
public function transform (AntiAttrition $antiattrition)
{
return[
'id' => $antiattrition->id,
'mo_id'=> $antiattrition->mo_id,
'assign_by'=> $antiattrition->assigned_by,
'id_clrt' => $antiattrition->id_clrt,
'TaskID'=> $antiattrition->TaskID,
'ACID'=> $antiattrition->ACID,
'CustIDNo'=> $antiattrition->CustIDNo
];
}
}
I want to show the result in json, like below
{
"data": [
{
"mo_id": "123",
"id_clrt": "10000000049",
"ACID": "123",
.....
},
{
"mo_id": "1234",
"id_clrt": "10000000045",
"ACID": "1235",
.....
},
{
"mo_id": "124",
"id_clrt": "10000000044",
"ACID": "1245",
.....
},
],
}
Please help me to solve this problem
In the foreach loop, avoid using same name for array and elements of the array you are iterating over, you can rename foreach($new as $new) to foreach($newArray as $new), or something meaningful with your code logic. Also rather than using $new[0] use $new['mo_id'] for referring to the key of the array

I need change return in laravel. object to array {} to [] (Error trying to diff '[object Object])

I need change return object to array in laravel api.
anyone guide? I am getting error, cant figure out why??
change {} to this []
ERROR Error: "Error trying to diff '[object Object]'. Only arrays and
iterables are allowed (API Laravel and front-end angular 7)
my code api Laravel
public function show($id)
{
$arCategoria = \App\Favorito::join('categoria', 'categoria.cd_categoria', '=', 'link.cd_categoria')
->select('*')
->where('categoria.cd_categoria_pai',$id)
->where('link.cd_usuario',$this->token['cd_usuario'])
->where('link.bo_ativo',true)
->get();
return (array)$this->processarCategoria($arCategoria);
}
public function processarCategoria($arCategoria){
$ar = array();
$cont = 0;
foreach($arCategoria as $key => $value){
$ar[$value['no_categoria'].'_'.$value['cd_categoria']][] = (array)array(
'no_link'=>$value['no_link'],
'cd_link'=>$value['cd_link'],
'vl_link'=>$value['vl_link'],
'bo_ativo'=>$value['bo_ativo'],
'link'=>$value['link']
);
$cont++;
}
return (array)$ar;
}
my return of laravel api
{
"Documentation_3": [
{
"no_link": "stackoverflow",
"cd_link": 5,
"vl_link": null,
"bo_ativo": 1,
"link": "https://stackoverflow.com"
},
{
"no_link": "Adventures of Time",
"link": "http://adventuresoftime.com.br"
}
],
"Things to buy_5": [
{
"no_link": "Games",
"link": "Games.com.br"
}
]
}
If dd($this->processarCategoria($arCategoria->toArray())) is a plain PHP array, which means it works.
Btw, I think your original code works,too, maybe you dd((array) $this->processarCategoria($arCategoria)) and check.
According to PHP.net, (array)$scalarValue is exactly the same as array($scalarValue)
According to Laravel document, The toArray() method converts the collection into a plain PHP array.
And the reason why the array become a JSON after return, as if someone using the json_encode PHP function, is because:
Laravel automatically convert the array into a JSON response, in
addition to returning strings from your routes and controllers.
Laravel document
I.e., laravel convert array to JSON, so it may show on the screen.
Route::get('/', function () {
//return is the same as dd()
//dd(json_encode([1=>'a', 2=>'b', 3=>'c']));
return [1=>'a', 2=>'b', 3=>'c'];
});

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;
}
}

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'];
}

Modifying JSON data for API

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;

Categories