web-dev-qa-db-fra.com

Laravel 5.6 - Transmettre des paramètres supplémentaires à l'API Resource?

Une Laravel API Resource peut être une ressource unique ou une collection. Dans certains cas, des paramètres supplémentaires doivent être transmis à la ressource/collection à partir du contrôleur. Vous trouverez ci-dessous un exemple simple illustrant le problème en utilisant User comme ressource unique/collection et un paramètre $Apple personnalisé à transmettre à la ressource pour la sortie. Le problème peut être vu dans la dernière Output (Collection) ci-dessous, où pour la valeur fruit, nous obtenons une valeur incorrecte de banana pour le premier utilisateur, au lieu de la valeur correcte Apple (que tous les autres utilisateurs obtiennent). Cela fonctionne parfaitement pour la sortie unique, mais pas pour la collection. Voir ci-dessous:

Contrôleur avec UserResource (Single)

$user = User::first();
return new UserResource($user, $Apple = true); // $Apple param passed

Contrôleur avec UserResource (Collection)

$users = User::limit(3)->get();
return UserResource::collection($users, $Apple = true); // $Apple param passed

UserResource

<?php

namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;

class UserResource extends JsonResource {
    private $Apple;

    public function __construct($resource, $Apple = false) {
        // Ensure we call the parent constructor
        parent::__construct($resource);
        $this->resource = $resource;
        $this->Apple = $Apple; // $Apple param passed
    }

    public function toArray($request) {
        return [
            'id'     => (int) $this->id, 
            'name'   => $this->name,
            'fruit'  => $this->Apple ? 'Apple' : 'banana',
        ];
    }
}

Sortie (Simple)

{
    "data": {
        "id": 1,
        "name": "Peter",
        "fruit": "Apple" // correct param!
    }
}

Sortie (Collection)

{
    "data": [
        {
            "id": 1,
            "name": "Peter",
            "fruit": "banana" // INCORRECT param!
        },
        {
            "id": 2,
            "name": "Lois",
            "fruit": "Apple" // correct param!
        },
        {
            "id": 3,
            "name": "Brian",
            "fruit": "Apple" // correct param!
        }
    ]
}

Veuillez noter qu'il ne s'agit que d'un exemple. Il peut s'agir d'une quantité quelconque de paramètres aléatoires (sans lien avec la collection User, mais doit être transmise pour la logique de sortie), telle qu'une valeur unique read_at timestamp d'une table différente que je souhaite transmettre une fois, et effectuez une logique dessus dans la collection de ressources avant la sortie (comme une comparaison avec un horodatage utilisateur), ou d'autres paramètres passés pour que la logique supplémentaire if/else soit exécutée dans le fichier de ressources en général afin de manipuler la sortie de la collection. Comment cela peut-il être fait?

3
Wonka

L'approche suivante a fonctionné pour moi:

UserResource

class UserResource extends Resource{

    protected $foo;

    public function foo($value){
        $this->foo = $value;
        return $this;
    }

    public function toArray($request){
        return [
            'id' => $this->id,
            'name' => $this->name,
            'foo' => $this->foo,
         ];
    }

    public static function collection($resource){
        return new UserResourceCollection($resource);
    }
}

UserCollection

class UserResourceCollection extends ResourceCollection{

    protected $foo;

    public function foo($value){
        $this->foo = $value;
        return $this;
    }

    public function toArray($request){
        return $this->collection->map(function(UserResource $resource) use($request){
            return $resource->foo($this->foo)->toArray($request);
    })->all();

        // or use HigherOrderCollectionProxy
        // return $this->collection->each->foo($this->foo)->map->toArray($request)->all()

        // or simple
        // $this->collection->each->foo($this->foo);
        // return parent::toArray($request);
    }
}

Différentes façons de passer le paramètre supplémentaire

(new UserResource($user))->foo('bar');
(new UserResourceCollection($user))->foo('bar');

UserResource::make($user)->foo('bar');
UserResourceCollection::make($users)->foo('bar');
UserResource::collection($users)->foo('bar');
8
Wonka

Vous pouvez transmettre les paramètres supplémentaires dans le cadre de l'appel du point de terminaison de l'API. Vous pouvez ensuite accéder aux paramètres avec l'objet $ request (pour votre exemple) dans UserResource. 

Par exemple, si vous appelez le noeud final à partir d'un client, tel qu'un navigateur Web, axios, etc., utilisez l'une des méthodes suivantes:

http://localhost:3000/api/users?apple=true

cela rendra le paramètre Apple avec une valeur true disponible dans le contrôleur. Sans aucune autre action de votre part, il sera alors également accessible dans le toArray ($ request) de UserResource. Vous pouvez y accéder semblable à:

public function toArray($request) {
      $isApple = $request->Apple;

        return [
            'id'     => (int) $this->id, 
            'name'   => $this->name,
            'fruit'  => $isApple ? 'Apple' : 'banana',
        ];
    }
0
SnapShot

Pour travailler avec Laravel 5.7, j'ai apporté quelques modifications par rapport à la réponse de Wonka

UserResource

class UserResource extends Resource{

    protected $foo;

    public function foo($value){
        $this->foo = $value;
        return $this;
    }

    public function toArray($request){
        return [
            'id' => $this->id,
            'name' => $this->name,
            'foo' => $this->foo,
         ];
    }

    public static function collection($resource){
        return new UserResourceCollection($resource, get_called_class());
    }
}

UserCollection

class UserResourceCollection extends AnonymousResourceCollection {

    protected $foo;

    public function foo($value){
        $this->foo = $value;
        return $this;
    }

    public function toArray($request){
        return $this->collection->map(function(UserResource $resource) use($request){
            return $resource->foo($this->foo)->toArray($request);
    })->all();

    }
}
0
Gabriel