【问题标题】:Including relationships in eloquent resource collection optionally可选地在 eloquent 资源集合中包含关系
【发布时间】:2019-12-22 12:04:05
【问题描述】:

我希望在集合 api 端点上选择性地加载关系

结束点将类似于

http://127.0.0.1:8000/api/posts/?include=comments includes comments with posts and I can add more using comma, like

http://127.0.0.1:8000/api/posts/?include=comments,images

但是当我没有传递这些查询参数时,它应该只返回 posts 和端点 http://127.0.0.1:8000/api/postshttp://127.0.0.1:8000/api/posts?page=10

RequestQueryFilter

<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
class RequestQueryFilter
{
    public function attach($resource, Request $request = null)
    {
        $request = $request ?? request();
        return tap($resource, function($resource) use($request) {
            $this->getRequestIncludes($request)->each(function($include) use($resource) {
                $resource->load($include);
            });
        });
    }
    protected function getRequestIncludes(Request $request)
    {
        // return collect(data_get($request->input(), 'include', [])); //single relationship
        return collect(array_map('trim', explode(',', data_get($request->input(), 'include', [])))); //multiple relationships
    }
}

助手

<?php
if ( ! function_exists('filter') ) {
    function filter($attach) 
    {
        return app('filter')->attach($attach);
    }
}
?>

PostController

public funciton index(Request $request) {
    $posts = Post::all();
    return new PostCollection(filter($posts));
}

PostCollection

return [
            'data' => $this->collection->transform(function($post){
                return [
                    'id' => $post->id,
                    'title' => $post->title,
                    'body' => $post->body,
                    'comments' =>  new CommentCollection($post->whenLoaded('comments')),
                     'images' =>  new ImageCollection($post->whenLoaded('images'))
                ];
            }),
        ];

显示

调用未定义的方法 App\Models\Post::whenLoaded()", 但如果我使用单一模型资源,它工作正常。

更新: 原因:- post collection 转换给了

 Collection gives 
 Post {#363
  #guarded: array:1 [
    0 => "id"
  ]
  #connection: "mysql"
  #table: "posts"
  #primaryKey: "id"
  #keyType: "int"
  +incrementing: true
  #with: []
  #withCount: []
  #perPage: 15
  +exists: true
  +wasRecentlyCreated: false
  #attributes: array:9 [

但是发布资源$this 给出了

Post {#344
  +resource: Post {#343
    #guarded: array:1 [
      0 => "id"
    ]
    #connection: "mysql"
    #table: "posts"
    #primaryKey: "id"
    #keyType: "int"
    +incrementing: true
    #with: []
    #withCount: []
    #perPage: 15
    +exists: true
    +wasRecentlyCreated: false
    #attributes: array:9 [

现在将 PostCollection $post 转换为 PostResource

 'data' => $this->collection->transform(function($post) use ($request) {
    $post = new PostResource($post);
    dd($post);

使用这个

'comments' =&gt; new CommentCollection($post-&gt;whenLoaded('comments')), 将始终返回 cmets,即使 include 中没有 comments

$post->relationLoaded('comments')

总是返回真。

【问题讨论】:

    标签: laravel eloquent eloquent-relationship laravel-eloquent-resource


    【解决方案1】:

    我不知道这是设计使然还是错误。我最好的猜测是它不适用于集合,因为whenLoaded($relation) 是资源类的方法,而不是模型本身。但作为一种解决方法,以下方法应该有效:

    return [
        'data' => $this->collection->transform(function ($post) {
            return [
               'id' => $post->id,
               'title' => $post->title,
               'body' => $post->body,
               'comments' => $this->when($post->relationLoaded('comments'), function () use ($post) {
                  return new CommentCollection($post->comments);
               }),
               'images' => $this->when($post->relationLoaded('images'), function () use ($post) {
                  return new ImageCollection($post->images);
               })
            ];
        }),
    ];
    

    由于您总是得到$post-&gt;relationLoaded('comments') 返回的true,我的建议是在您的基本模型上创建一个新方法(或者如果您没有,则使用Post 模型),它不仅检查加载状态以及关系的内容:

    public function relationLoadedAndNotEmpty(string $relation): bool
    {
        return $this->relationLoaded($relation) && 
               $this->getRelation($relation) instanceof \Illuminate\Support\Collection &&
               $this->getRelation($relation)->isNotEmpty();
    }
    

    按照上面的建议在资源中使用此方法而不是 ´$post->relationLoaded('cmets')` 应该会给您想要的结果,因为如果集合为空,它也会避免打印属性。

    【讨论】:

    • 我已经试过了,它会返回独立于查询参数的关系。
    • 这听起来很奇怪。不应根据您的代码加载关系。你有没有机会在你的Post 模型中使用protected $with = ['comments', 'images']
    • 顺便说一下,您应该过滤允许加载的关系...添加某种白名单。
    • 同意你的观点,但这与这个问题无关。
    • 这就是为什么除了我的其他问题之外它是一个额外的评论。 :)
    猜你喜欢
    • 2016-03-25
    • 2019-10-20
    • 1970-01-01
    • 1970-01-01
    • 2011-06-04
    • 2014-01-22
    • 2017-04-23
    • 1970-01-01
    • 2015-08-27
    相关资源
    最近更新 更多