Loading more eloquent hasMany relation queries through ajax in Laravel?

By | January 12, 2018
Questions:

On my site, users can post “statuses”, and other users can reply/comment on those statuses. On the front page of my site, for example, I have the most liked statuses of the past 3 days displayed.

In my HomeController.php:

$statuses = Status::where('is_image', 1)
    ->where('is_mature', 0)
    ->where('created_at', '>=', Carbon::now()->subHours(168))
    ->orderBy('like_count', 'desc')
    ->take(6)
    ->get();

Statuses can also have replies. Replies use the same table as the status table. Replies are pretty much just statuses with a “parent” status, and regular statuses are just statuses without a “parent”.

From the model status.php:

public function scopeNotReply($query) {
    return $query->whereNull('parent_id');
}

public function replies() {
    return $this->hasMany('CommendMe\Models\Status', 'parent_id');
}

So back to my home page, here’s how the statuses are loaded on the home page:

home.blade.php:

@if ($statuses->count())
    @foreach ($statuses as $status)
      <div class="post-container">

        ...

        blah blah blah

        ...

        @foreach ($status->replies->reverse()->slice(0, 3) as $reply)
            <div class="status-reply-content">
                ...

                blah blah blah

                ...
            </div>
        @endforeach

        @if (count($status->replies) > 3)
            <a href="#">
                    View More Comments ({{ count($status->replies) }})
            </a>
        @endif

      </div>
    @endforeach
@endif

What I want is for the “View More Comments” button to load another set of 3 comments.

This would be simple if these were being paginated. In fact, elsewhere on my site I load more images through ajax. Like so:

if($request->ajax()) {
    return [
        'images' => view('browse.partials.fullview_partial')->with('images', $images->appends(request()->only('query')))->render(),
        'next_page' => $images->nextPageUrl()
    ];
}       

however this relies on loading in the next set of pagination through ajax. Whereas the $status->replies relationship is not being paginated.

So how could I load more replies in this scenario?

Edit:

Even an alternate solution would help.

Answers:

Do you need to load the first page of replies server-side? I guess since you are willing to load subsequent pages with AJAX you could just handle all client-side and just have an AJAX endpoint that provides the data.

Just make ajax calls to your endpoint passing the status id and page number.

// AjaxController
public function replies()
{
    $status = Status::findOrFail(request('id'));
    return $status->replies()->paginate(3);
}

(The page number will be resolved automatically.)

This will return a JSON representation of the paginator, which has the urls for previous and next pages, and the data for the current page. Then just render the data client-side.

Questions:
Answers:

What I ended up doing was simply creating a new function in my controller for the request itself, and then sending the ID of the status along with it, along with a skip count (that increases based on how many replies are already loaded) to skip the loaded replies.

public function getReplies($statusID, $skip, Request $request) {

    $status = Status::findOrFail($statusID);
    $replies = $status->replies()->orderBy('created_at', 'desc')->skip($skip)->take(10)->get();

    if($request->ajax()) {
        return [
            'replies' => view('templates.partials.replies')->with('replies', $replies)->render(),
        ];
    }   
}

Leave a Reply

Your email address will not be published. Required fields are marked *