I want to use laravel-datatables-fractal for transforming server-side response using Fractal.
First I wrote a transformer for Product model like this :
class ProductTransformer extends TransformerAbstract
{
protected $availableIncludes = [
'prices'
];
public function transform(Product $product)
{
$transforms = [
'product_id' => $product->product_id,
'code' => $product->code,
'title' => $product->title,
'description' => $product->description,
];
return $transforms;
}
public function includePrices(Product $product)
{
$prices = $product->prices;
return $this->collection($prices, new PriceTransformer, FALSE);
}
}
As you can see there is an included prices attribute that uses another transformer named PriceTransformer.
And in the ProductController that makes and returns result of all products as a datatable formatted response I have a datatable method like this:
public function datatable(Request $request)
{
return app('datatables')->of(Product::all())
->setTransformer(new ProductTransformer)
// ->setSerializer(new CustomArraySerializer())
->make(TRUE);
}
Response is returned but a problem that I have is : when I want to return prices included attribute , it returned as a data attribute like this :
{
"draw": 0,
"recordsTotal": 8,
"recordsFiltered": 8,
"data": [
{
"product_id": 1,
"title": "Product Title 1",
"for_sale": true,
"for_purchase": true,
"changeable": false,
"returnable": false,
"sale_price": "40000.00",
"purchase_price": "5000.00",
"creator": 1,
"created_at": "2017-12-11 12:21:49",
"updated_at": "2017-12-14 11:55:52",
"prices": {
"data": [
{
"sale_price": "30000.00",
"created_at": "2017-12-11 12:21:49"
},
{
"sale_price": "40000.00",
"created_at": "2017-12-11 12:39:00"
},
]
}
},
{
"product_id": 11,
"title": "Product Title 11",
"for_sale": true,
"for_purchase": true,
"changeable": false,
"returnable": false,
"sale_price": "50000.00",
"purchase_price": "40000.00",
"creator": 1,
"created_at": "2017-12-16 11:07:43",
"updated_at": "2017-12-16 11:07:43",
"prices": {
"data": [
{
"sale_price": "50000.00",
"created_at": "2017-12-16 11:07:43"
}
]
}
}
],
"input": {
"include": "prices"
}
}
I want to remove data from included attributes and they be in this format for example :
"prices": [
{
"sale_price": "50000.00",
"created_at": "2017-12-16 11:07:43"
}
]
Even I added a serializer (as you see in above code and is commented) with below content that I used it for all other responses and worked well :
class CustomArraySerializer extends ArraySerializer
{
public function collection($resourceKey, array $data)
{
if ($resourceKey === FALSE) {
return $data;
}
return ['success' => TRUE, 'result' => $data];
}
public function item($resourceKey, array $data)
{
if ($resourceKey === FALSE) {
return $data;
}
return ['success' => TRUE, 'result' => $data];
}
}
But when use it I got this error :
{
"draw": 0,
"recordsTotal": 8,
"recordsFiltered": 0,
"data": [],
"error": "Exception Message:\n\nUndefined index: data"
}
What is problem and how can solve that ?
I think this is because you are returning prices as collection? If I recall this correctly, you can use item instead for a single response?
return $this->collection($prices, new PriceTransformer, FALSE);
// to something like
return $this->item($prices, new PriceTransformer, FALSE);
beacause each product has many prices we should use collection instead item in this case. although I used item but got same error again.
I think when included attribute ha
Ok, maybe we can work around with this using editColumn:
->editColumn('prices', function ($product) {
return $product->prices ... // transform to an array without the data key.
});
I haven't use fractal much so I can't recall much on it. Maybe using a custom serializer for the prices would do the trick too?
I used :
->editColumn('prices', function ($product) {
return $product->prices;
})
returns prices with a data key.
and used :
->editColumn('prices', function ($product) {
return $product->prices->data;
})
returns this error :
{
"draw": 0,
"recordsTotal": 8,
"recordsFiltered": 0,
"data": [],
"error": "Exception Message:\n\nProperty [data] does not exist on this collection instance."
}
seems that problem is in this codes :
public function transform($output, $transformer, $serializer = null)
{
if ($serializer !== null) {
$this->fractal->setSerializer($this->createSerializer($serializer));
}
$collector = [];
foreach ($transformer as $transform) {
if ($transform != null) {
$resource = new Collection($output, $this->createTransformer($transform));
$collection = $this->fractal->createData($resource)->toArray();
$transformed = $collection['data']; //****Problem is here****
$collector = array_map(function ($item_collector, $item_transformed) {
if ($item_collector === null) {
$item_collector = [];
}
return array_merge($item_collector, $item_transformed);
}, $collector, $transformed);
}
}
return $collector;
}
@yajra , I used suggested way in this answer and problem solved .
Glad you sorted this out. 馃憤
Unfortunately, I found that there is another problem when Passing through datatable instance a fractal transformed and serialized collection.
Suppose there is :
$products = Product::all();
$products = fractal()
->collection($products)
->serializeWith(new \Spatie\Fractalistic\ArraySerializer())
->transformWith(new ProductTransformer)
->toArray();
return app('datatables')->of($products)
->make(TRUE);
And transformer is like :
public function transform(Product $product)
{
$transforms = [
'product_id' => $product->product_id,
'title' => $product->title,
'for_sale' => (boolean) $product->for_sale,
'for_purchase' => (boolean) $product->for_purchase,
'changeable' => (boolean) $product->changeable,
'returnable' => (boolean) $product->returnable,
'created_at' => (string)$product->created_at,
'updated_at' => (string)$product->updated_at,
];
return $transforms;
}
Problem is that datatable ignored (boolean) type casting and return same 0 , 1 that exists on the database.
For all other types it works fine.
Just curios, have you tried using it like below?
$products = Product::all();
return app('datatables')->of($products)
->setTransformer(new ProductTransformer)
->setSerializer(new \Spatie\Fractalistic\ArraySerializer())
->toJson();
I tried but got same error that I refers to that in first issue :
{
"draw": 0,
"recordsTotal": 8,
"recordsFiltered": 0,
"data": [],
"error": "Exception Message:\n\nUndefined index: data"
}
Can you please try updating the suspected code:
$transformed = $collection['data'];
// to
$transformed = $collection['data'] ?? $collection;
And use this snippet again:
$products = Product::all();
return app('datatables')->of($products)
->setTransformer(new ProductTransformer)
->setSerializer(new \Spatie\Fractalistic\ArraySerializer())
->toJson();
@yajra , you'r awesome.
bu changing to :
$transformed = $collection['data'] ?? $collection;
problem is solved.
Patch released on v1.1.1, kindly update and check again.