Laravel 11 Like Dislike System Tutorial Example

By Hardik Savani June 15, 2024 Category : Laravel

In this tutorial, I will show you how to create like and dislike system in laravel 11 application.

In this example, we won't use any special packages to make a like-dislike system. We'll make our own like-dislike system for posts. We'll use Laravel UI to set up user accounts. Then, we'll create a posts table with some example posts. Next, we'll make a page that shows a list of posts with titles and descriptions. On this list page, we'll add thumbs-up and thumbs-down icons so users can like or dislike the posts. We'll use AJAX to handle the likes and dislikes.

You can create your example by following a few steps:

Step for Laravel 11 Create Like Dislike System Example

  • Step 1: Install Laravel 11
  • Step 2: Create Posts and Likes Tables
  • Step 3: Create and Update Models
  • Step 4: Create Dummy Posts
  • Step 5: Create Auth using Scaffold
  • Step 6: Create Routes
  • Step 7: Create Controller
  • Step 8: Create and Update Blade Files
  • Run Laravel App

Step 1: Install Laravel 11

This step is not required; however, if you have not created the Laravel app, then you may go ahead and execute the below command:

composer create-project laravel/laravel example-app

Step 2: Create Posts and Likes Tables

Here, we will create posts and likes table with model. so, let's run the following command:

php artisan make:migration create_posts_table

php artisan make:migration create_likes_table

now, let's update the following migrations:

database/migrations/2024_06_11_035146_create_posts_table.php

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->text('body');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('posts');
    }
};

database/migrations/2024_06_13_175106_create_likes_table.php

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('likes', function (Blueprint $table) {
            $table->id();
            $table->foreignId('post_id')->constrained()->onDelete('cascade');
            $table->foreignId('user_id')->constrained()->onDelete('cascade');
            $table->boolean('like'); // true for like, false for dislike
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('likes');
    }
};

now, Let's run the migration command:

php artisan migrate

Step 3: Create and Update Models

Here, we will create Post and Like model using the following command. we also need to update User model here. we will write relationship and some model function for like and dislike.

php artisan make:model Post

php artisan make:model Like

now, update the model file with hasMany() relationship:

app/Models/Post.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    use HasFactory;

    protected $fillable = ['title', 'body'];

    /**
     * Write code on Method
     *
     * @return response()
     */
    public function likes()
    {
        return $this->hasMany(Like::class)->where('like', true);
    }

    /**
     * Write code on Method
     *
     * @return response()
     */
    public function dislikes()
    {
        return $this->hasMany(Like::class)->where('like', false);
    }
}

app/Models/Like.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Like extends Model
{
    use HasFactory;

    protected $fillable = ['post_id', 'user_id', 'like'];

    /**
     * Write code on Method
     *
     * @return response()
     */
    public function post()
    {
        return $this->belongsTo(Post::class);
    }

    /**
     * Write code on Method
     *
     * @return response()
     */
    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

app/Models/User.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable
{
    use HasFactory, Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name',
        'email',
        'password',
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * Get the attributes that should be cast.
     *
     * @return array
     */
    protected function casts(): array
    {
        return [
            'email_verified_at' => 'datetime',
            'password' => 'hashed',
        ];
    }

    /**
     * Write code on Method
     *
     * @return response()
     */
    public function likes()
    {
        return $this->hasMany(Like::class);
    }

    /**
     * Write code on Method
     *
     * @return response()
     */
    public function hasLiked($postId)
    {
        return $this->likes()->where('post_id', $postId)->where('like', true)->exists();
    }

    /**
     * Write code on Method
     *
     * @return response()
     */
    public function hasDisliked($postId)
    {
        return $this->likes()->where('post_id', $postId)->where('like', false)->exists();
    }

    /**
     * Write code on Method
     *
     * @return response()
     */
    public function toggleLikeDislike($postId, $like)
    {
        // Check if the like/dislike already exists
        $existingLike = $this->likes()->where('post_id', $postId)->first();

        if ($existingLike) {
            if ($existingLike->like == $like) {
                $existingLike->delete();

                return [
                    'hasLiked' => false,
                    'hasDisliked' => false
                ];
            } else {
                $existingLike->update(['like' => $like]);
            }
        } else {
            $this->likes()->create([
                'post_id' => $postId,
                'like' => $like,
            ]);
        }

        return [
            'hasLiked' => $this->hasLiked($postId),
            'hasDisliked' => $this->hasDisliked($postId)
        ];
    }
}

Step 4: Create Dummy Posts

In this step, we need to run the migration command to create the seeder to create dummy records in posts table

Let's run the migration command:

php artisan make:seeder CreateDummyPost

noww, we need to update CreateDummyPost seeder.

database/seeders/CreateDummyPost.php

<?php

namespace Database\Seeders;

use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use App\Models\Post;

class CreateDummyPost extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        $posts = [
            [
                'title' => 'Laravel Product CRUD Tutorial',
                'body' => 'Step by Step Laravel Product CRUD Tutorial'
            ],
            [
                'title' => 'Laravel Image Upload Example',
                'body' => 'Step by Step Laravel Image Upload Example'
            ],
            [
                'title' => 'Laravel File Upload Example',
                'body' => 'Step by Step Laravel File Upload Example'
            ],
            [
                'title' => 'Laravel Cron Job Example',
                'body' => 'Step by Step Laravel Cron Job Example'
            ],
            [
                'title' => 'Laravel Send Email Example',
                'body' => 'Step by Step Laravel Send Email Example'
            ],
            [
                'title' => 'Laravel CRUD with Image Upload',
                'body' => 'Step by Step Laravel CRUD with Image Upload'
            ],
            [
                'title' => 'Laravel Ajax CRUD with Image Upload',
                'body' => 'Step by Step Laravel Ajax CRUD with Image Upload'
            ],
            [
                'title' => 'Laravel Ajax CRUD with Image Upload',
                'body' => 'Step by Step Laravel Ajax CRUD with Image Upload'
            ]
        ];

        foreach ($posts as $key => $value) {
            Post::create([
                'title' => $value['title'],
                'body' => $value['body'],
            ]);
        }
    }
}

now, the run seeder using the following command:

php artisan db:seed --class=CreateDummyPost

Step 5: Create Auth using Scaffold

Now, in this step, we will create an auth scaffold command to generate login, register, and dashboard functionalities. So, run the following commands:

Laravel 11 UI Package:

composer require laravel/ui 

Generate Auth:

php artisan ui bootstrap --auth 

npm install

npm run build

Step 6: Create Routes

In this step, we will create routes for like unlike system. So we require to create following route in web.php file.

routes/web.php

<?php

use Illuminate\Support\Facades\Route;

use App\Http\Controllers\PostController;

Route::get('/', function () {
    return view('welcome');
});

Auth::routes();

Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])->name('home');

Route::middleware('auth')->group(function () {
    Route::get('posts', [PostController::class, 'index'])->name('posts.index');
    Route::post('posts/ajax-like-dislike', [PostController::class, 'ajaxLike'])->name('posts.ajax.like.dislike');
});

Step 7: Create Controller

now in PostController, we will add two new method posts() and ajaxLike(). so let's see PostController like as bellow:

app/Http/PostController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Post;

class PostController extends Controller
{
    /**
     * Show the application dashboard.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        $posts = Post::get();
        return view('posts', compact('posts'));
    }


    /**
     * Show the application dashboard.
     *
     * @return \Illuminate\Http\Response
     */
    public function ajaxLike(Request $request)
    {
        $post = Post::find($request->id);
        $response = auth()->user()->toggleLikeDislike($post->id, $request->like);

        return response()->json(['success' => $response]);
    }
}

Step 8: Create and Update Blade Files

In this step, we will update app.blade.php file and create posts.blade file. so, let's update it.

resources/views/layouts/app.blade.php

<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- CSRF Token -->
    <meta name="csrf-token" content="{{ csrf_token() }}">

    <title>{{ config('app.name', 'Laravel') }}</title>

    <!-- Fonts -->
    <link rel="dns-prefetch" href="//fonts.bunny.net">
    <link href="https://fonts.bunny.net/css?family=Nunito" rel="stylesheet">

    <!-- Scripts -->
    @vite(['resources/sass/app.scss', 'resources/js/app.js'])

    @yield('style')
</head>
<body>
    <div id="app">
        <nav class="navbar navbar-expand-md navbar-light bg-white shadow-sm">
            <div class="container">
                <a class="navbar-brand" href="{{ url('/') }}">
                    Laravel Like DisLike System Example
                </a>
                <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}">
                    <span class="navbar-toggler-icon"></span>
                </button>

                <div class="collapse navbar-collapse" id="navbarSupportedContent">
                    <!-- Left Side Of Navbar -->
                    <ul class="navbar-nav me-auto">

                    </ul>

                    <!-- Right Side Of Navbar -->
                    <ul class="navbar-nav ms-auto">
                        <!-- Authentication Links -->
                        @guest
                            @if (Route::has('login'))
                                <li class="nav-item">
                                    <a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a>
                                </li>
                            @endif

                            @if (Route::has('register'))
                                <li class="nav-item">
                                    <a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a>
                                </li>
                            @endif
                        @else
                            <li class="nav-item">
                                <a class="nav-link" href="{{ route('posts.index') }}">{{ __('Posts') }}</a>
                            </li>
                            <li class="nav-item dropdown">
                                <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
                                    {{ Auth::user()->name }}
                                </a>

                                <div class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdown">
                                    <a class="dropdown-item" href="{{ route('logout') }}"
                                       onclick="event.preventDefault();
                                                     document.getElementById('logout-form').submit();">
                                        {{ __('Logout') }}
                                    </a>

                                    <form id="logout-form" action="{{ route('logout') }}" method="POST" class="d-none">
                                        @csrf
                                    </form>
                                </div>
                            </li>
                        @endguest
                    </ul>
                </div>
            </div>
        </nav>

        <main class="py-4">
            @yield('content')
        </main>
    </div>
</body>

@yield('script')

</html>

resources/views/posts.blade.php

@extends('layouts.app')

@section('style')
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css" crossorigin="anonymous" referrerpolicy="no-referrer" />
<style type="text/css">
    i{
        cursor: pointer;
    }
</style>
@endsection

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-12">
            <div class="card">
                <div class="card-header">{{ __('Posts List') }}</div>

                <div class="card-body">
                    
                    <div class="row">
                        @foreach($posts as $post)
                        <div class="col-md-3">
                            <div class="card mt-2" style="width: 18rem;">
                              <img src="https://picsum.photos/id/0/367/267" class="card-img-top" alt="...">
                              <div class="card-body">
                                <h5 class="card-title">{{ $post->title }}</h5>
                                <p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
                                <div class="like-box">
                                    <i id="like-{{ $post->id }}" 
                                        data-post-id="{{ $post->id }}"
                                        class="like fa-thumbs-up {{ auth()->user()->hasLiked($post->id) ? 'fa-solid' : 'fa-regular' }}"></i> 
                                    <span class="like-count">{{ $post->likes->count() }}</span>
                                    <i id="like-{{ $post->id }}" 
                                        data-post-id="{{ $post->id }}"
                                        class="dislike fa-thumbs-down {{ auth()->user()->hasDisliked($post->id) ? 'fa-solid' : 'fa-regular' }}"></i> 
                                    <span class="dislike-count">{{ $post->dislikes->count() }}</span>
                                </div>
                              </div>
                            </div>
                        </div>
                        @endforeach
                    </div>

                </div>
            </div>
        </div>
    </div>
</div>
@endsection

@section('script')
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js" referrerpolicy="no-referrer"></script>
<script type="text/javascript">
    $(document).ready(function() {     

        $.ajaxSetup({
            headers: {
                'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
            }
        });

        $('.like-box i').click(function(){    
            var id = $(this).attr('data-post-id');
            var boxObj = $(this).parent('div');
            var c = $(this).parent('div').find('span').text();
            var like = $(this).hasClass('like') ? 1 : 0;

            $.ajax({
               type:'POST',
               url: "{{ route('posts.ajax.like.dislike') }}",
               data:{ id:id, like:like },
               success:function(data){

                    if (data.success.hasLiked == true) {

                        if($(boxObj).find(".dislike").hasClass("fa-solid")){
                            var dislikes = $(boxObj).find(".dislike-count").text();
                            $(boxObj).find(".dislike-count").text(parseInt(dislikes)-1);
                        }

                        $(boxObj).find(".like").removeClass("fa-regular");
                        $(boxObj).find(".like").addClass("fa-solid");

                        $(boxObj).find(".dislike").removeClass("fa-solid");
                        $(boxObj).find(".dislike").addClass("fa-regular");

                        var likes = $(boxObj).find(".like-count").text();
                        $(boxObj).find(".like-count").text(parseInt(likes)+1);

                    } else if(data.success.hasDisliked == true){

                        if($(boxObj).find(".like").hasClass("fa-solid")){
                            var likes = $(boxObj).find(".like-count").text();
                            $(boxObj).find(".like-count").text(parseInt(likes)-1);
                        }

                        $(boxObj).find(".like").removeClass("fa-solid");
                        $(boxObj).find(".like").addClass("fa-regular");

                        $(boxObj).find(".dislike").removeClass("fa-regular");
                        $(boxObj).find(".dislike").addClass("fa-solid");

                        var dislike = $(boxObj).find(".dislike-count").text();
                        $(boxObj).find(".dislike-count").text(parseInt(dislike)+1);
                    } else {
                        if($(boxObj).find(".dislike").hasClass("fa-solid")){
                            var dislikes = $(boxObj).find(".dislike-count").text();
                            $(boxObj).find(".dislike-count").text(parseInt(dislikes)-1);
                        } 

                        if($(boxObj).find(".like").hasClass("fa-solid")){
                            var likes = $(boxObj).find(".like-count").text();
                            $(boxObj).find(".like-count").text(parseInt(likes)-1);
                        }

                        $(boxObj).find(".like").removeClass("fa-solid");
                        $(boxObj).find(".like").addClass("fa-regular");

                        $(boxObj).find(".dislike").removeClass("fa-solid");
                        $(boxObj).find(".dislike").addClass("fa-regular");

                    }
               }
            });

        });   

    }); 
</script>
@endsection

Run Laravel App:

All the required steps have been done, now you have to type the given below command and hit enter to run the Laravel app:

php artisan serve

Now, Go to your web browser, type the given URL and view the app output:

http://localhost:8000/

Output:

You can download the code from git:

Download Code from Github

I hope it can help you...

Shares