Включение отношений в коллекцию красноречивых ресурсов по желанию

Я хочу загрузить отношения опционально на конечной точке 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/posts а также http://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()", но если я использую ресурс одной модели, он работает нормально.

Обновление: Причина:- преобразование после сбора дает

 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' => new CommentCollection($post->whenLoaded('comments')), всегда будет возвращать комментарии, даже нет comments в include

$post->relationLoaded('comments')

всегда возвращает истину

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

Как вы всегда получаете true вернулся $post->relationLoaded('comments'), я предлагаю создать новый метод на вашей базовой модели (или, если у вас его нет, Post модель), которая проверяет не только состояние загрузки, но и содержимое отношения:

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

Использование этого метода в ресурсе, как предложено выше, вместо "$post-> RelationsLoaded('comments')` должно дать вам желаемые результаты, поскольку это также позволит избежать печати атрибута, если коллекция пуста.

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