In this tutorial, we will use Livewire to create a component for Like/Dislike, similar to YouTube or any social network. We will show the count of likes and dislikes, also minimizing the number of queries to the DB.
Laravel Project Preparation
We'll show the list of posts and the number of votes for every post.
For this demo, we'll use our own Laravel Breeze Pages Skeleton which will give use Breeze-like layout but with a public page for posts list.
First, we need a Post
Model, Controller, and View. For now, without any Livewire.
php artisan make:model Post -mc
database/migrations/xxxx_create_posts_table.php:
return new class extends Migration { public function up(): void { Schema::create('posts', function (Blueprint $table) { $table->id(); $table->string('title'); $table->text('text'); $table->timestamps(); }); }};
app/Models/Post.php:
class Post extends Model{ protected $fillable = [ 'title', 'text', ];}
app/Http/Controllers/PostController.php:
class PostController extends Controller{ public function __invoke(): View { $posts = Post::latest()->paginate(); return view('posts', compact('posts')); }}
And a simple Blade file to show the list of the posts.
resources/views/posts.blade.php:
<x-app-layout> <x-slot name="header"> <h2 class="text-xl font-semibold leading-tight text-gray-800 dark:text-gray-200"> {{ __('Posts') }} </h2> </x-slot> <div class="py-12"> <div class="mx-auto max-w-7xl sm:px-6 lg:px-8"> <div class="overflow-hidden bg-white shadow-sm dark:bg-gray-800 sm:rounded-lg"> <div class="p-6 text-gray-900 dark:text-gray-100"> @foreach($posts as $post) <h3 class="text-xl font-medium">{{ $post->title }}</h3> <p>{{ $post->text }}</p> <hr class="my-4"> @endforeach {{ $posts->links() }} </div> </div> </div> </div></x-app-layout>
Next, we need to save votes. For this, we will create a Vote
Model.
php artisan make:model Vote -m
database/migrations/xxxx_create_votes_table.php:
return new class extends Migration { public function up(): void { Schema::create('votes', function (Blueprint $table) { $table->id(); $table->foreignId('post_id')->constrained()->cascadeOnDelete(); $table->foreignId('user_id')->constrained()->cascadeOnDelete(); $table->smallInteger('vote'); $table->timestamps(); }); }};
app/Models/Vote.php:
class Vote extends Model{ protected $fillable = [ 'post_id', 'user_id', 'vote', ];}
Now let's add Vote
relations to the Post
Model.
app/Models/Post.php:
use Illuminate\Database\Eloquent\Relations\HasOne;use Illuminate\Database\Eloquent\Relations\HasMany; class Post extends Model{ protected $fillable = [ 'title', 'text', ]; public function votes(): HasMany { return $this->hasMany(Vote::class); } public function userVotes(): HasOne { return $this->votes()->one()->where('user_id', auth()->id()); } }
We'll need two relations. The first one is just a regular One To Many
relation which we will use to create and update the vote for the post.
The second one is more interesting: the userVotes
relation will return null
if a user hasn't voted yet. Otherwise, it will return the Vote
model from which we will be able to tell if a user liked or disliked a post.
Livewire Component
Now let's create a Livewire component.
php artisan make:livewire LikeDislike
First, let's add the Livewire component after the post text and...