@Andre548

Как правильно получить ответы к комментариям?

Пытаюсь сделать вложенные комментарии.
Миграция:
public function up()
    {
        Schema::create('comments', function (Blueprint $table) {
            $table->id();
            $table->text('description');
            $table->unsignedBigInteger('parent_id')->nullable();
            $table->foreignId('user_id')->nullable()->index()->constrained('users');
            $table->foreignId('post_id')->nullable()->index()->constrained('posts');
            $table->timestamps();
        });
    }

Модель Post
class Post extends Model
{
    use HasFactory;

    protected $table = 'posts';
    protected $guarded = false;

    public function category()
    {
        return $this->belongsTo(Category::class, 'category_id', 'id');
    }

    public function getImageMainAttribute()
    {
        return url('storage/' . $this->main_image);
    }

    public function user()
    {
        return $this->belongsTo(User::class);
    }

    public function comments()
    {
        return $this->hasMany(Comment::class)->whereNull('parent_id');
    }
}

Модель Comment
class Comment extends Model
{
    use HasFactory;

    protected $table = 'comments';
    protected $guarded = false;

    public function user()
    {
        return $this->belongsTo(User::class, 'user_id', 'id');
    }

    public function post()
    {
        return $this->belongsTo(Post::class, 'post_id', 'id');
    }

    public function replies()
    {
        return $this->hasMany(Comment::class, 'parent_id');
    }
}

При таком запросе я получаю данные.
$post = Post::where('id', $id)->with([
            'comments.user:id,name',
            'comments.replies.user:id,name',
            'comments.replies.replies.user:id,name',
            'comments.replies.replies.replies.user:id,name',
        ])->get()->toArray();

Но работаю с ресурсами, и не могу понять как туда правильно передать данные. Я получаю только родительский комментарий, как тут к нему привязать ответы?
class PostResource extends JsonResource
{
    public function toArray($request)
    {
        return [
            "id" => $this->id,
            "title" => $this->title,
            "description" => $this->description,
            "main_image" => $this->imageMain,
            "category" => $this->category,
            "created_at" => Carbon::parse($this->created_at)->format(' F d, Y'),
            "comments" => $this->comments,
        ];
    }
}
  • Вопрос задан
  • 104 просмотра
Пригласить эксперта
Ответы на вопрос 1
gzhegow
@gzhegow
aka "ОбнимиБизнесмена"
1. Для начала избавьтесь от replies.replies.replies.... Для этого есть паттерн для баз данных Closure Table, которая позволит вам выбрать
->with([
  'comments.replies.replies_closure' => function ($q) { $q->where('parent_id', 1)->where('depth', '>', 0); },
  'comments.replies.replies_closure.parent' => function ($q) { $q->where('user_id', 1); },
])

Минус - лишняя таблица, определенный геморой при добавлении комментов, но если гуглить Closure Table на сайте percona.com - там есть три запроса, которые можно скопировать и вставить для "создания", "перемещения в другой родитель" и "отвязки". Бонус - вы получаете неограниченную вложенность комментариев, не требующую рекурсии вообще. Можете хоть всё дерево залпом выводить. Однако стоит обратить внимание, что Ютуб выводит только первый уровень не просто так. Комментов может быть оч. много, и в каждом оч. много ответов, можно просто ушатать лимит памяти сервера на скрипт через полгода использования проектов, когда к какому-то посту будет 5000 комментов уровней на 12. Тостер вот вышел из положения разумнее - он просто сказал, что максимум есть комменты и ответы к ним, и они не вкладываются, зато можно цитировать. Очень правильно и не глючит.

2. Для вопроса "привязать комментарии" - когда вы сделаете выборку, для вашего ресурса - $this->comments уже будет содержать то что вы выбирали. Метод toArray() рекурсивно пройдет по всему, что попало в ресурс (должен по крайней мере так делать). Если не делает - может стоит написать $this->comments->toArray();
Если в каждом из $this->comments поле 'replies' пустое, значит что-то не так выбрано.

3. Деревья при выводе гораздо разумнее выдавать в структуре
$list[ $id ] = $model;
$tree[ $parent_id ][ $child_id ] = true;
// $parents[ $child_id ] = $parent_id; // если есть вероятность, что дерево перестроят на той стороне и вернут обратно новое

чем в бесконечной вложенности, это минимизирует повторения и уменьшает размер выходного json-а.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы