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.
Questions
- What am I doing wrong?
- What is a reliable fix for this issue?
2
Answers
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.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:
Access the passed object using
$commentOrReply
and adjust conditions: