Search code examples
phplaravellaravel-livewire

How to make pagination for reviews/comments section of a post in a livewire component


The following code will display reviews for each post, and allow a user to make a review

What i want to be able to do is paginate where the reviews/comments is being looped out part in the component

To start, here is my PostsController show function to get posts

    public function show($id)
    {
        //defining out post object
        $post = Post::find($id);
        
        return view('posts.show')->with('post', $post);
    }

Next, here is my show.blade file with the livewire component

  <!-- livewire posts component -->
  <livewire:cafe-review-section :post="$post" />
        

Next, here is my component - the component shows all comments/reviews for a post, along with a form to post a review/comment

<div>
    <!-- show comment form -->
    <form wire:submit.prevent="postReview" action=" " method="post">
        @csrf
        <textarea wire:model.defer="cafeReview" required name="cafeReview" id="" cols="30" rows="4"
                  placeholder="Type review here"
        ></textarea>

        @error('comment')
        <p>{{ $message }}</p>
        @enderror

        <button type="submit" class="btn btn-primary">
            <div  wire:loading wire:target="postReview" class="spinner-border" role="status">
                <span class="visually-hidden">Loading...</span>
            </div>
            <span>post comment</span>
        </button>
    </form>

    <!-- here is where we will display a list of comments for the post-->
    @if(count($post->cafeReviews) > 0)
        @foreach($post->cafeReviews->sortDesc() as $comm)
            <div class="d-flex bd-highlight" style="background-color: #FAF0E6; margin-bottom: 1%; border-radius: 20px; padding-top: 1%; padding-bottom: 2%;">
                <div class="p-2 flex-shrink-1 bd-highlight">
                    <img style="width: 60px; height: 60px; vertical-align: middle" src="/css/img/user.png" alt="avatar">
                </div>
                <div class="p-2 w-100 bd-highlight">
                    <div class="flex items-center">
                        <div>Posted by: <b>{{ $comm->username }}</b></div>
                        <hr>
                        <div class="text-gray-700 mt-2">{{ $comm->content }}</div>
                    </div>
                    <div class="d-flex" style="margin-top: 3%; padding-right: 1%;">
                        <div class="ms-auto">
                            {{ $comm->created_at->diffForHumans() }}
                        </div>
                    </div>
                </div>
            </div>
        @endforeach
    @else
        <p>no reviews</p>
    @endif
</div>

Next, here is my livewire class

<?php

namespace App\Http\Livewire;

use App\Models\CafeReview;
use App\Models\Post;
use Livewire\Component;
use RealRashid\SweetAlert\Facades\Alert;

class CafeReviewSection extends Component
{

    public $post;

    //keeping track of review with sate
    public $cafeReview;

    //defining our validation rules
    protected $rules = ([
        'cafeReview' => 'required|min:4'
    ]);

    //we dont actually need this as livewire knows already
    public function mount(Post $post)
    {
        $this->post = $post;

    }

    public function postReview()
    {
        //validating our comment state when we submit
        sleep(1);
        $this->validate();
        
        CafeReview::create([
            'post_id' => $this->post->id,
            'user_id' => auth()->user()->id,
            'username' => auth()->user()->username,
            'content' => $this->cafeReview,
        ]);

        $this->post->refresh();

        $this->cafeReview = '';

    }

    public function render()
    {
        return view('livewire.cafe-review-section');

    }
}


Solution

  • If you follow the livewire docs for pagination it should be easy to add it to your view.

    <!-- here is where we will display a list of comments for the post-->
    @forelse ($reviews as $review)
      <div class="d-flex bd-highlight" style="background-color: #FAF0E6; margin-bottom: 1%; border-radius: 20px; padding-top: 1%; padding-bottom: 2%;">
        <div class="p-2 flex-shrink-1 bd-highlight">
          <img style="width: 60px; height: 60px; vertical-align: middle" src="/css/img/user.png" alt="avatar">
        </div>
        <div class="p-2 w-100 bd-highlight">
          <div class="flex items-center">
            <div>Posted by: <b>{{ $comm->username }}</b></div>
            <hr>
            <div class="text-gray-700 mt-2">{{ $comm->content }}</div>
          </div>
          <div class="d-flex" style="margin-top: 3%; padding-right: 1%;">
            <div class="ms-auto">
              {{ $comm->created_at->diffForHumans() }}
            </div>
          </div>
        </div>
      </div>
    @empty
      <p>no reviews</p>
    @endforelse
    <!-- pagination links -->
    {!! $reviews->links() !!}
    
    <?php
    
    namespace App\Http\Livewire;
    
    use App\Models\CafeReview;
    use App\Models\Post;
    use Livewire\Component;
    // WithPagination Trait
    use Livewire\WithPagination;
    use RealRashid\SweetAlert\Facades\Alert;
    
    class CafeReviewSection extends Component
    {
        // withPagination Trait
        use WithPagination;
    
        public $post;
    
        //keeping track of review with sate
        public $cafeReview;
    
        //defining our validation rules
        protected $rules = ([
            'cafeReview' => 'required|min:4'
        ]);
    
        //we dont actually need this as livewire knows already
        public function mount(Post $post)
        {
            $this->post = $post;
        }
    
        public function postReview()
        {
            //validating our comment state when we submit
            sleep(1);
            $this->validate();
    
            // create review through post->cafeReviews relationship
            $this->post->cafeReviews()->create([
                'user_id' => auth()->user()->id,
                'username' => auth()->user()->username,
                'content' => $this->cafeReview,
            ]);
    
            // Reset pagination after creating new review
            $this->resetPage();
    
            $this->cafeReview = '';
        }
    
        public function render()
        {
            // Append paginated reviews to view
            $reviews = $this->post->cafeReviews()->latest()->paginate(10);
    
            return view('livewire.cafe-review-section', compact('reviews'));
        }
    }