Добавление фильтра в красноречивый ресурс для условного прикрепления отношений

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

3 ответа

Я хочу загрузить отношения условно, как

Lazy Eager Загрузка с помощью load() вызов

Lazy Eager Loading выполняет те же конечные результаты, что и with() в Ларавеле, однако, не автоматически. Например:

?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()требуется только в том случае, если вы определили свою модель для автоматической загрузки отношений, которые вы хотите загрузить условно.

Со всеми данными, отфильтрованными в Контроллере, просто убедитесь, что отображаются только загруженные отношения в вашем PostResource

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

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

    return $data;
}

Я бы создал собственный ресурс для постов с

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

Частичное решение

Для этого потребуется небольшое изменение в классе ресурса

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()", если кто-нибудь предложит полное решение, это будет очень полезно, если я смогу это сделать, я обновлю его здесь.

Другие вопросы по тегам