Home > Software design >  What makes the AJAX call in this Laravel application fail?
What makes the AJAX call in this Laravel application fail?

Time:09-07

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

  1. What causes this bug?
  2. What is the easiest fix?

CodePudding user response:

The error can be due to the following issues

  1. Missing CSRF token ( Which seems not being sent using your AJAX )
  2. Wrong Route or Conflict with another route
  3. Route Param value not passing Correctly
  4. 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.

  • Related