thephpleague / fractal

Output complex, flexible, AJAX/RESTful data structures.
fractal.thephpleague.com
MIT License
3.52k stars 351 forks source link

Included data and Serializer #142

Closed matiux closed 9 years ago

matiux commented 9 years ago

Hi, I don't know if is a my error, but I noticed that the serializer that I added, works (fine) only in main data that I retrieved. Please, see the example below

Route: /user
Serializer: ArraySerializer

{
"id": 1,
"username": "matiux",
"name": "Matthew",
"role": "waiter",
"active": 1
}

But if I include some references, the serializer does not works:

Route: /user?include=restaurants
Serializer: ArraySerializer

{
"id": 1,
"username": "matiux",
"name": "Matteo",
"role": "Cameriere",
"active": 1,
"restaurants": {
data": [
{
"id": 1,
"name": "Restaurant name",
"openDate": "15/03/2014",
"closeDate": "15/02/2015",
"restDate": 2
}
]
}
}

You can see the "restaurants" key with the "data" subkey that I would not like to have

jasonlewis commented 9 years ago

You would need to paste the serializer that you're using. Probably a mixture of how you've done the included data and collections I'd wager.

matiux commented 9 years ago

Ok, some pieces of my code:

Controller


...
$this->fractalManager           = new Manager();

$this->fractalManager->setSerializer(new ArraySerializer());

if (Input::get('include'))
   $this->fractalManager->parseIncludes(Input::get('include'));
....
// $user id a Doctrine object
$user               = $this->repo->getById($this->user->getId());
$resource           = new Item($user, new UserTransformer());
return $this->fractalManager->createData($resource)->toArray();

UserTransformer

...

    protected $availableIncludes = [
        'restaurants'
    ];

    public function transform(\User $user) {

        return [

            'id'            => $user->getId(),
            'username'      => $user->getUsername(),
            'name'          => $user->getName(),
            'role'          => $user->getRole()->getName(),
            'active'        => (int) $user->getActive(),

        ];
    }

    public function includeRestaurants(\User $user) {

        $restaurants    = $user->getRestaurants();

        return $this->collection($restaurants, new RestaurantTransformer);
    }
jasonlewis commented 9 years ago

Oh wait you're using the ArraySerializer that Fractal ships with? I believe it would be due to this line that you're seeing that. However, if you were to roll your own ArraySerializer and remove the data key then returning a collection will never be keyed. Again, this may be want you want.

You can extend the ArraySerializer and simply overload the collection method.

class MyArraySerializer extends League\Fractal\Serializer\ArraySerializer
{
    public function collection($resourceKey, array $data)
    {
        return $data;
    }
}

Then simply set the serializer on your League\Fractal\Manager instance.

$fractal->setSerializer(new MyArraySerializer);

Again, returning a collection will result in a response similar to this:

[
    {
        "id": 1,
        "name": "Jason"
    },
    {
        "id": 2,
        "name": "Phil"
    }
]
matiux commented 9 years ago

Ok, thank you so much!

ghost commented 9 years ago

It looks like removing the "data" envelope is a common request - could it perhaps be the default behaviour for ArraySerializer?

I understand the reason for having it in DataArraySerializer and why DataArraySerializer is the default, but not sure I understand why you'd ever want the inconsistency you get with ArraySerializer items vs collections.

I don't need metadata for my current project, and if that changes later on, my thinking is I can just put it in custom header(s) instead.

Many thanks @jasonlewis for the MyArraySerializer example!