Laravel 5.6 - 如何向API资源传递额外参数?

36

Laravel的API资源可以是单个资源或集合。在某些情况下,需要从控制器向资源/集合传递额外的参数。以下是一个简单的示例,使用User作为单个/集合资源,并使用自定义的$apple参数传递到资源以进行输出。问题可以在最后的输出(集合)中看到,在其中对于fruit值,我们得到了第一个用户的错误值为banana,而不是正确的apple值(所有其他用户都得到)。它对于单个输出完美地工作,但对于集合则不然。请参见以下内容:

带UserResource的控制器(单个)

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

使用UserResource(集合)的控制器

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

输出(单个)

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

输出(集合)

    {
        "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!
            }
        ]
    }

请注意,这只是一个示例,它可以是任何数量的随机参数(与User集合无关,但必须传递给输出逻辑),例如来自不同表格的单个值read_at时间戳,我想在资源集合中传递一次,并在输出之前对其进行一些逻辑处理(如与用户时间戳比较),或者传递其他参数进行附加逻辑if/else以便在资源文件中进行操作,以操纵集合的输出。怎样做呢?


1
集合功能代码在哪里? - Bhavin Thummar
在 Laravel 代码库中,我认为需要一个已经熟悉 Laravel 资源(上面链接了)的人,他曾经遇到过这个问题并解决了它。 - Wonka
这个不起作用。 - gileneusz
9个回答

59

以下方法适用于我:

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

用户收藏

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

传递额外参数的不同方式

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

6
我尝试了这个 UserResource::collection($users)->foo('bar');,但是出现了错误 Method Illuminate\Support\Collection::foo does not exist. - Munna Khan
@MunnaKhan foo不是静态方法。您正在将foo用作静态方法。您需要首先实例化UserResource集合,就像答案中那样。 - Zugor

21

这是我使用 Laravel 8 制作的方法。



class PatientResource extends JsonResource
{

  private static $data;
  /**
   * Transform the resource into an array.
   *
   * @param \Illuminate\Http\Request $request
   * @return array
   */
  public function toArray($request)
  {
    //access $data
    //self::$data
    return [
      'id' => $this->id,
      'first_name' => $this->first_name,
      'middle_name' => $this->middle_name,
      'last_name' => $this->last_name,
      'contact_number' => $this->contact_number
    ];
  }

  //I made custom function that returns collection type
  public static function customCollection($resource, $data): \Illuminate\Http\Resources\Json\AnonymousResourceCollection
  {
   //you can add as many params as you want.
    self::$data = $data;
    return parent::collection($resource);
  }
}

然后在我的控制器中调用了这个自定义函数。

$data = PatientResource::customCollection($query->get(),$medicines);

完美的,谢谢。 - Zeross

11
你可以在调用API端点时将额外的参数作为其一部分传递。然后,在UserResource中使用$request对象(对于你的示例)访问这些参数。
例如,如果你从Web浏览器、axios等客户端调用端点,可以使用以下内容:
```` ... axios.get('/api/user/1', { params: { foo: 'bar' } }) .then(function (response) { console.log(response); }) .catch(function (error) { console.log(error); }); ... ````
http://localhost:3000/api/users?apple=true

这将使参数“apple”的值为true在控制器中可用。除此之外,您无需采取任何其他操作,它也可以在UserResource的toArray($request)中访问。您可以通过类似以下的方式访问它:

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

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

1
太完美了!我为什么没想到呢。加油!! - Anonymous-E
我认为这应该是被采纳的答案,因为它是内置的! - Adem Tepe

9
这个简单的技巧在Laravel中对我很有效 :) 控制器
$user = User::find($user->id);
$user->access_token = $tokenResult->accessToken; // Add additional data
return new ProfileResource($user);

资源

public function toArray($request)
{
    return [
        'id'            => $this->id,
        'picture'       => $this->picture,
        'first_name'    => $this->first_name,
        'last_name'     => $this->last_name,
        'active'        => $this->active,
        'access_token'  => isset($this->access_token) ? $this->access_token : '', // Additional data
    ];
}

更新(2022年6月)相同的逻辑也适用于集合

User.php(模型)

public static function getUserList(Request $request)
{
    $output = [];

    $query = User::query();

    if ($request->has('search')) {
        $query->where('first_name', $request->input('search'));
        $query->orWhere('last_name', $request->input('search'));
        $query->orWhere('email', $request->input('search'));
    }

    $queryActive   = clone $query;
    $queryInactive = clone $query;

    $output = $query->paginate(config('services.PAGINATE_PER_PAGE'));

    $statistics = [
        'total'    => $query->count(),
        'inactive' => $queryInactive->where('status', 0)->count(),
        'active'   => $queryActive->where('status', 1)->count(),
    ];

    $output->statistics = $statistics;

    return $output;
}

UserController.php

public function index(Request $request): UserCollection
{
    $users = User::getUserList($request);

    return new UserCollection($users);
}

UserResource.php

namespace App\Http\Resources;

use Carbon\Carbon;
use Illuminate\Http\Resources\Json\JsonResource;

class UserResource extends JsonResource
{
    public function toArray($request)
    {
        return [
            'first_name'                => $this->first_name,
            'last_name'                 => $this->last_name,
            'full_name'                 => $this->first_name . '' . $this->last_name,
            'email'                     => $this->email,
            'mobile'                    => $this->mobile,
            'gender'                    => $this->gender,
            'status'                    => $this->status,
            'status_title'              => $this->status ? 'Active' : 'Inactive',
            'created_at_simple'         => Carbon::parse($this->created_at)->format('Y-m-d'),
            'created_at_simple_string'  => Carbon::parse($this->created_at)->toFormattedDateString(),
            'created_at'                => Carbon::parse($this->created_at)->format('Y-m-d H:i:s'),
            'created_at_ago'            => Carbon::parse($this->created_at)->diffForHumans(),
            'created_at_string'         => Carbon::parse($this->created_at)->toDayDateTimeString(),
            'updated_at_simple'         => Carbon::parse($this->updated_at)->format('Y-m-d'),
            'updated_at_simple_string'  => Carbon::parse($this->updated_at)->toFormattedDateString(),
            'updated_at'                => Carbon::parse($this->updated_at)->format('Y-m-d H:i:s'),
            'updated_at_ago'            => Carbon::parse($this->updated_at)->diffForHumans(),
            'updated_at_string'         => Carbon::parse($this->updated_at)->toDayDateTimeString(),
            'roles'                     => RoleResource::collection($this->roles),
        ];
    }
}

UserCollection.php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\ResourceCollection;

class UserCollection extends ResourceCollection
{
    public function toArray($request)
    {
        return [
            'data'             => $this->collection,
            'statistics'       => [
                'filters'    => request()->all(),
                'active'     => $this->statistics['active'],
                'inactive'   => $this->statistics['inactive'],
                'total'      => $this->collection->count(),
            ],
        ];
    }
}

2
集合方面怎么样?我有一些数据,我通过 all() 基于它们进行获取,然后像这样返回:DataResource :: collection($data) - Atlas-Pio
没听懂你的问题,你遇到了什么问题? - zarpio
这不是资源收集。 - Anonymous-E
对于集合,我已经更新了答案。 - zarpio
你在2022年的示例中传递了什么额外的参数/数据?还是access_token吗?我正在疯狂地尝试弄清楚它是如何传递的。 - St. Jan
非常感谢您对这个答案的努力,但是它太含糊或冗长了。这个答案更好一些:https://dev59.com/w1UL5IYBdhLWcg3wOl9x#51689732 - jovialcore

4
To works with Laravel 5.7,我对Wonka's answer做了一些相关的更改。 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());
        }
    }

用户集合

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

        }
    }

3

我通过在以下位置获取$request->get('param')解决了我的问题:

public function toArray($request){ 

   $param = $request->get('param');

   ... 

} 

不要通过资源传递参数。


2

你可以黑客攻击请求

在控制器中,你有这个

request()->request->add([ 'extra_data' => [ 'foo' => 'foo-value', 'other' => 'something', ], ]);

而在资源中

request()->get('extra_data') or $request->get('extra_data')

1
你可以使用 Laravel 8
来存储额外的功能。
    return (UserResource::make(User::find($user->id)))
            ->additional([
                'message'=>[
                    ['user by name: '.$user->name.' created successfull.']
                ]
            ])->response()->setStatusCode(201);

0

Laravel 8 上进行了测试

你不能在 API 资源 集合 上使用 additional()

下面的示例不起作用 ✗

ProductSizeResource::collection()->additional(['stocks' => $this->stocks]);

你需要逐个资源添加额外的数据。
ProductSizeResource::collection(
   $product->sizes->map(
       fn(ProductSize  $productSize) => (new ProductSizeResource($productSize))->additional(['stocks' => $this->stocks])
   )
)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接