I am working on a blogging application in Laravel 8.
The ArticlesController controller I have this method to display the single article and its comments:
class ArticlesController extends FrontendController {
// More code
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 = Comment::where(['article_id' => $article->id, 'approved' => 1])->orderBy('id', 'desc');
$comments = $commentsQuery->paginate(10);
$comments_count = $commentsQuery->count();
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,
'tagline' => $article->title,
])
);
}
}
In the view I have this for the comments list:
<div id="commentsList">
<ol >
@foreach ($comments as $comment)
<li >
<div >
<img src="{{ asset('images/avatars/' . $comment->user->avatar) }}" alt="" width="50" height="50">
</div>
<div >
<div >
<div >{{ $comment->user->first_name }} {{ $comment->user->last_name }}</div>
<div >
<div >{{ date('jS M Y', strtotime($comment->created_at)) }}</div>
<div >
<a href="#0">Reply</a>
</div>
</div>
</div>
<div >
<p>{{ $comment->body }}</p>
</div>
</div>
</li>
@endforeach
</ol>
<div >
loading...
</div>
</div>
The routes related to the article(s):
// Article routes
Route::get('/', [ArticlesController::class, 'index'])->name('homepage');
Route::get('/category/{category_id}', [ArticlesController::class, 'category'])->name('category');
Route::get('/author/{user_id}', [ArticlesController::class, 'author'])->name('author');
Route::get('/show/{slug}', [ArticlesController::class, 'show'])->name('show');
The goal
I want to replace the comments pagination with an "infinite scroll".
For this purpose, I have:
/* Infinite comments
* ------------------------------------------------------ */
function infiniteComments() {
var page = 1;
$(window).scroll(function() {
if ($(window).scrollTop() $(window).height() >= $(document).height() - $('.s-footer').height()) {
page ;
loadMoreData(page);
}
});
}
function loadMoreData(page){
var base_url = window.location.href.split('?')[0];
$.ajax({
url: `${base_url}?page=${page}`,
type: "get",
beforeSend: function() {
$('.ajax-load').show();
}
})
.done(function(data) {
if (data.html == "") {
$('.ajax-load').hide();
return;
}
$('.ajax-load').hide();
$(".infinite-scroll").append(data.html);
})
.fail(function(jqXHR, ajaxOptions, thrownError) {
console.log('The server is not responding...');
});
}
$(document).ready(function(){
infiniteComments();
});
The problem
While accessing https://larablog.com/show/deserunt-qui-exercitationem?page=2
shows the comments on page 2 correctly, the Chrome console shows these 500 (Internal Server Error) errors:
https://larablog.com/show/deserunt-qui-exercitationem?page=65 500 (Internal Server Error)
The server is not responding...
https://larablog.com/show/deserunt-qui-exercitationem?page=76 500 (Internal Server Error)
The server is not responding...
The error can be tracked back to this error message in ArticlesController, at line 70 - $article = Article::firstWhere('slug', $slug)
:
Trying to get property 'id' of non-object.
This is strange because $article = Article::firstWhere('slug', $slug)
works fine without Ajax.
Questions
- What causes this bug?
- What is the easiest fix?
CodePudding user response:
The error can be due to the following issues
- Missing CSRF token ( Which seems not being sent using your AJAX )
- Wrong Route or Conflict with another route
- Route Param value not passing Correctly
- Database Record missing or wrong field name.
Try the below example to debug to get your issue fixed
Always try to use the try-catch block to make Debugging easy for your self like the below example of your ArticlesController Code Please use the below code and then check your Logs file under the directory
storage/logs/laravel.log or file with laravel-28-08-2022.log
Then use that error log to find the actual cause of the 500 Internal Server Error
class ArticlesController extends FrontendController {
// More code
public function show($slug) {
try{
// 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 = Comment::where(['article_id' => $article->id, 'approved' => 1])->orderBy('id', 'desc');
$comments = $commentsQuery->paginate(10);
$comments_count = $commentsQuery->count();
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,
'tagline' => $article->title,
])
);
}
catch(\Exception $e){
\Log::error("Error in file: ".$e->getFile()." , Error Message: ".$e->getMessage());
return abort(500);
}
}
}
CodePudding user response:
firstWhere
returns the first record which meets the passed criteria, defaulting to null
. So, your line of
$article = Article::firstWhere('slug', $slug);
will return the first article whose slug
matches the $slug
, or null
, if no such record exists. Now, whenever you reference $article->id
, you assume that $article
is a proper Article
and you wonder about the value of its id
. This will yield the error you have experienced if there is no matching article.
So, it is wise to check for empty($article)
just after $article
was initialized and handle the edge-case when it is empty indeed.