skip to Main Content

I have made a blogging application in Laravel 8.

The articles support comments and comment replies. I am currently working on the edit/update comments functionality.

In the Comment model, I "merge" comments and replies like so:

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

In the controller, I have the below method for getting the comments with replies:

private function get_commentQuery(int $article_id, int $limit = 0, int $offset = 0): object
{
  $commentQuery = Comment::where(['article_id' => $article_id, 'approved' => 1])
    ->orderBy('id', $this->comments_orderby_direction)
    ->with('replies', function ($query) {
      $query->where('approved', 1);
    });

  if ($offset > 0) {
    $commentQuery = $commentQuery->offset($offset);
  }
  if ($limit > 0) {
    $commentQuery = $commentQuery->limit($limit);
  }

  return $commentQuery;
}

In comments-list.blade.php I have:

@foreach ($comments as $comment)
    @if (null == $comment->parent_id)
        <li class="depth-1 comment">
            <div class="comment__avatar">
                <img class="avatar" src="{{ asset('images/avatars/' . $comment->user->avatar) }}"
                    alt="{{ $comment->user->first_name }} {{ $comment->user->last_name }}" width="50" height="50">
            </div>
            <div class="comment__content">
                <div class="comment__info">
                  <div class="comment__author">{{ $comment->user->first_name }} {{ $comment->user->last_name }}</div>
                  <div class="comment__meta">
                      <div class="comment__time">{{ date('jS M Y', strtotime($comment->created_at)) }}</div>
                      @auth
                        <div class="comment__reply">
                          @if ($comment->user->id !== Auth::user()->id)
                              <a class="comment-reply-link" href="#0">
                                <i class="fa fa-comment"></i> Reply
                            </a> 
                          @endif
                            
                          @if ($comment->user->id == Auth::user()->id)
                              <a href="#0" class="comment-edit-link">
                                <i class="fa fa-edit"></i> Edit
                              </a>
                              @include('themes/' . $theme_directory . '/partials/comment-delete-form')
                          @endif
                        </div>
                      @endauth
                  </div>
                </div>
                <div class="comment__text">
                  <p>{{ $comment->body }}</p>
                </div>
            </div>

            @auth
              @include('themes/' . $theme_directory . '/partials/comment-form')

              @if ($comment->user->id == Auth::user()->id)
                @include('themes/' . $theme_directory . '/partials/comment-edit-form')
              @endif
            @endauth

            {{-- Comment replies --}}
            @if (count($comment->replies))
                <ul class="children">
                    @foreach ($comment->replies as $reply)
                        <li class="depth-2 comment">
                            <div class="comment__avatar">
                                <img class="avatar" src="{{ asset('images/avatars/' . $reply->user->avatar) }}"
                                    alt="{{ $comment->user->first_name }} {{ $comment->user->last_name }}"
                                    width="50" height="50">
                            </div>
                            <div class="comment__content">
                                <div class="comment__info">
                                    <div class="comment__author">{{ $reply->user->first_name }}
                                        {{ $reply->user->last_name }}</div>
                                    <div class="comment__meta">
                                        <div class="comment__time">{{ date('jS M Y', strtotime($reply->created_at)) }}
                                        </div>
                                        @auth
                                            <div class="comment__reply">
                                              @if ($reply->user->id == Auth::user()->id)
                                                <a href="#0" class="comment-edit-link">
                                                  <i class="fa fa-edit"></i> Edit
                                                </a>
                                                @include('themes/' . $theme_directory . '/partials/comment-delete-form')
                                              @endif
                                            </div>
                                        @endauth
                                    </div>
                                </div>
                                <div class="comment__text">
                                    <p>{{ $reply->body }}</p>
                                </div>
                            </div>

                            @auth
                              @if ($reply->user->id == Auth::user()->id)
                                @include('themes/' . $theme_directory . '/partials/comment-edit-form')
                              @endif
                            @endauth
                        </li>
                    @endforeach
                </ul>
            @endif
        </li>
    @endif
@endforeach

My intention is to use one form template for editing both comments and replies. I have added the form for editing comments in a partial called comment-edit-form.blade.php:

<form class="commentEditForm" method="post" action="{{ route('comment.update', !isset($reply) ? $comment->id : $reply->id) }}" autocomplete="off" novalidate> 
    @csrf
      <fieldset>
        <div class="message form-field">
          <textarea name="msg" id="message" class="h-full-width" placeholder="Your Message" required>{{ !isset($reply) ? $comment->body : $reply->body }}</textarea>

          @error('msg')
          <p class="help-block text-danger">{{ $message }}</p>
          @enderror
        </div>
        <br>
        <input name="submit" id="submit" class="btn btn--primary btn-wide btn--large h-full-width" value="Update Comment" type="submit">
      </fieldset>
  </form>

As is visible above, I have tried to differentiate comments from replies because, of course, they have different ids and bodies.

The problem

For a reason I was unable to figure out, all comments that come after a comment reply in the foreach loop, are prepopulated with the reply’s body.

enter image description here

Questions

  1. What am I doing wrong?
  2. What is a reliable fix for this issue?

2

Answers


  1. Your problem comes from variable scope within your Blade template. When you include the comment-edit-form.blade.php partial within the loop that iterates over comments and replies, the $reply from the previous iteration is still accessible in the scope of the next iteration. As a result, even when processing a primary comment (not a reply), the $reply variable is set and the form gets populated with the reply’s body. To resolve this, you need to ensure that the $reply is not mistakenly used for primary comments. One way to do this is by explicitly passing the relevant comment or reply to the partial.

    Login or Signup to reply.
  2. Problem:

    The $reply variable’s scope within the @include('themes/' . $theme_directory . '/partials/comment-edit-form') call is causing the problem. It persists between iterations, leading to incorrect pre-population of subsequent comment forms.

    Solution:

    Pass the relevant comment or reply object to the partial expicitly:

    @if ($comment->user->id == Auth::user()->id)
      @include('themes/' . $theme_directory . '/partials/comment-edit-form', ['commentOrReply' => $comment])
    @endif
    
    @foreach ($comment->replies as $reply)
      @if ($reply->user->id == Auth::user()->id)
        @include('themes/' . $theme_directory . '/partials/comment-edit-form', ['commentOrReply' => $reply])
      @endif
    @endforeach
    

    Access the passed object using $commentOrReply and adjust conditions:

    <form class="commentEditForm" method="post" action="{{ route('comment.update', $commentOrReply->id) }}" autocomplete="off" novalidate>
        @csrf
        <fieldset>
            <div class="message form-field">
                <textarea name="msg" id="message" class="h-full-width" placeholder="Your Message" required>{{ $commentOrReply->body }}</textarea>
            </div>
        </fieldset>
    </form>
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search