Laravel-datatables: remove data key from included attributes

Created on 21 Dec 2017  路  13Comments  路  Source: yajra/laravel-datatables

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 ?

bug

All 13 comments

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.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

SGarridoDev picture SGarridoDev  路  3Comments

hohuuhau picture hohuuhau  路  3Comments

ghost picture ghost  路  3Comments

jackrsantana picture jackrsantana  路  3Comments

hari-web picture hari-web  路  3Comments