2

Laravel 5.8 PHP 7.4

我想有条件地加载关系

http://127.0.0.1:8000/api/posts 

http://127.0.0.1:8000/api/posts/1 are my end points now, I want to load comments like

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

http://127.0.0.1:8000/api/posts/1/?include=comments

如果查询参数在那里,那么它应该加载带有帖子的评论,或者它应该只加载帖子/帖子

我通过参考博客文章来做到这一点

现在,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 show(Request $request, Post $post) {
    return new PostResource(filter($post));
}

但是当我试图检索

http://127.0.0.1:8000/api/posts/1/?include=comments getting no comments, with no error in log

解决方法是 PostResource

 public function toArray($request)
    {
        // return parent::toArray($request);
        $data = [
            'id' => $this->id,
            'name' => $this->title,
            'body' => $this->content,
        ];

        $filter = $request->query->get('include', '');

        if($filter){
          $data[$filter] = $this->resource->$filter;
        }

        return $data;
    }
4

3 回答 3

3

我想有条件地加载关系

load()使用调用延迟加载

with()Lazy Eager Loading 完成了与Laravel相同的最终结果,但是,不是自动的。例如

?include=comments

// Get all posts.
$posts = Post::without('comments')->all();

if (request('include') == 'comments')) {
    $posts->load('comments');
}

return PostResource::collection($posts);

或者,您可以要求include查询字符串是一个数组:

?include[]=comments&include[]=tags

// Validate the names against a set of allowed names beforehand, so there's no error.
$posts = Post::without(request('includes'))->all();

foreach (request('includes') as $include) {
    $posts->load($include);
}

return PostResource::collection($posts);

仅当您将模型定义为自动急切加载您想要有条件地加载的关系时才需要调用。without()

在 Controller 中过滤所有数据后,只需确保在您的PostResource

public function toArray($request) {
    $data = [...];

    foreach ($this->relations as $name => $relation)
    {
        $data[$name] = $relation;
    }

    return $data;
}
于 2019-08-13T22:30:19.477 回答
1

我会为帖子创建一个自定义资源

php artisan make_resource 

命令。例如 PostResource。资源的 toArray 函数必须返回数据。

PostResource.php

public function toArray($request){
     $data =['title' => $this->resource->title,

    'body' => $this->resource->body,

    'images' => new ImageCollection($this->whenLoaded('images')),
            ];

     $filter = $request->query->get('filter', '');

     if($filter){
      $data['comments'] => new CommentCollection($this->resource->comments);

     }
  return $data;
}

此外,对于集合,您需要创建一个 ResourceCollection。

PostResourceCollection.php

class PostResourceCollection extends ResourceCollection
{
    /**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request
     * @return array
     */
    public function toArray($request)
    {
        return [
            'data' => $this->collection,
        ];
    }
}

在您的控制器中: PostsController.php

   //show one post
     public function show(Post $post, Request $request)
        {

           /**this response is for API or vue.js if you need to generate view, pass the resource to the view */
            return $this->response->json( new PostResource($post));
        } 
    //list of posts
        public function index(Request $request)
            {
               $posts = Post::all();
               /**this response is for API or vue.js if you need to generate view, pass the resource to the view */
                return $this->response->json( new PostResourceCollection($posts));
            } 
于 2019-08-13T13:21:11.493 回答
0

部分解决方案

它需要对资源类进行小的更改

public function toArray($request)
{
    // return parent::toArray($request);
    $data = [
        'id' => $this->id,
        'title' => $this->title,
        'body' => $this->body,
        'comments' => new CommentCollection($this->whenLoaded('comments')),
        'images' => new ImageCollection($this->whenLoaded('images')),
    ];
    return $data;
}

如果已加载,它将加载评论和图像,这取决于include查询参数,如果不包括在内,则不会加载关系。

然而,

在后期收藏中

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\Customer::whenLoaded()”,如果有人提出完整的解决方案,这将是一个很大的帮助,如果我能做到,我会在这里更新。

于 2019-08-14T02:25:37.013 回答