I have made a blogging application in Laravel 8.
On the single article view, I am displaying comments and comment replies.
There is an option to load comments via AJAX, on page scroll.
When the “infinite scroll” option is checked, the first 10 comments are present on page load, the rest are loaded via AJAX.

In ArticlesController.php I have:
class ArticlesController extends FrontendController {
public function show( $slug ) {
// Single article
$article = Article::firstWhere( 'slug', $slug );
$old_article = Article::where( 'id', '<', $article->id )->orderBy( 'id', 'DESC' )->first();
$new_article = Article::where( 'id', '>', $article->id )->orderBy( 'id', 'ASC' )->first();
// Comments
$commentsQuery = $this->get_commentQuery( $article->id );
$comments_count = $commentsQuery->count();
// If infinite scroll, paginate comments (to be loaded one page per scroll),
// Else show them all
if (boolval($this->is_infinitescroll)) {
$comments = $commentsQuery->paginate($this->comments_per_page);
} else {
$comments = $commentsQuery->get();
}
return view( 'themes/' . $this->theme_directory . '/templates/single',
array_merge( $this->data, [
'categories' => $this->article_categories,
'article' => $article,
'old_article' => $old_article,
'new_article' => $new_article,
'comments' => $comments,
'comments_count' => $comments_count,
'comments_per_page' => $this->comments_per_page,
'tagline' => $article->title,
'is_infinitescroll' => $this->is_infinitescroll
] )
);
}
/**
* AJAX Call for Loading extra comments
*
* @param Request $request
*
* @return void
*/
public function get_comments_ajax( Request $request ) {
if ( ! $request->ajax() ) {
exit();
}
$more_comments_to_display = TRUE;
$article_id = $request->post( 'article_id' );
$page_number = $request->post( 'page' );
$offset = $this->comments_per_page * $page_number;
$data['comments'] = $this->get_commentQuery( $article_id, $this->comments_per_page, $offset )->get();
$content = '';
if ( $data['comments']->count() ) {
$content .= view('themes/' . $this->theme_directory . '/partials/comments-list',
array_merge( $data, [
'is_infinitescroll' => $this->is_infinitescroll
])
);
} else {
$more_comments_to_display = FALSE;
}
echo json_encode( [ 'html' => $content, 'page' => $page_number, 'more_comments_to_display' => $more_comments_to_display, 'article_id' => $article_id ] );
exit();
}
/**
* get_commentQuery
*
* @param int $article_id
* @param int $limit
* @param int $offset
*
* @return object
*/
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 infinite-comments.js I have:
/* Infinite comments
* ------------------------------------------------------ */
$(document).ready(function () {
let flagMoreCommentsToDisplay = true;
let flagCommentsBlockNewRequest = false;
let domInfiniteScroll = $(".infinite-scroll");
infiniteComments();
function infiniteComments() {
let page = 0;
$(window).scroll(function () {
if (flagCommentsBlockNewRequest === false) {
if ($(window).scrollTop() + $(window).height() >= $(document).height() - $('.s-footer').height()) {
if (flagMoreCommentsToDisplay) {
flagCommentsBlockNewRequest = true;
page++;
loadMoreData(page);
}
}
}
});
}
function loadMoreData(page) {
let base_url = window.location.origin
$.ajax({
url: base_url + '/load_comments',
type: 'POST', dataType: 'json',
data: {'_token': token, 'page': page, 'article_id': article_id},
beforeSend: function () {
$('.ajax-load').show();
}
})
.done(function (data) {
$('.ajax-load').hide();
let commentHtml = data.html;
flagMoreCommentsToDisplay = data.more_comments_to_display;
if (flagMoreCommentsToDisplay) {
if (commentHtml !== '') {
domInfiniteScroll.append(commentHtml);
}
}
flagCommentsBlockNewRequest = false;
})
.fail(function () {
flagCommentsBlockNewRequest = false;
});
}
});
In the blade file that displays the comments – comments-list.blade.php:
@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">
<a class="comment-reply-link" href="#0">Reply</a>
</div>
@endauth
</div>
</div>
<div class="comment__text">
<p>{{ $comment->body }}</p>
</div>
</div>
@auth
@include('themes/' . $theme_directory . '/partials/comment-form')
@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>
</div>
</div>
<div class="comment__text">
<p>{{ $reply->body }}</p>
</div>
</div>
</li>
@endforeach
</ul>
@endif
</li>
@endif
@endforeach
The problem
On page scroll, loading more comments fails with a 500 error:
POST https://larablog.com/load_comments 500 (Internal Server Error)
Questions
- What is my mistake?
- What is the most reliable way to fix this issue?