Cara Membuat Sistem Komentar Dengan Laravel 7

Cara Membuat Sistem Komentar Dengan Laravel 7

Pendahuluan

Membangun sebuah aplikasi yang membutuhkan feedback dari penggunanya, seperti blog, e-commerce dan lain sebagainya membutuhkan sistem komentar untuk saling interaksi antara pemilik dan pengguna atau antara pengguna dengan pengguna lainnya. Artikel ini juga sebagai tanggapan atas permintaan dari pembaca Daengweb.id untuk menjelaskan bagaimana cara membuat sistem komentar dengan PHP atau Laravel 7.

Schema yang akan digunakan sebagai sampel adalah aplikasi blog dimana kita akan membuat data dummy postingan dan masing-masing postingan memiliki komentar dari pengguna. Setiap komentar bisa di-reply sebanyak mungkin tapi cakupannya hanya dalam 1 tingkatan.

membuat sistem komentar laravel

Baca Juga: Cara Upload File ke Amazon S3 Menggunakan Laravel 7

Instalasi & Kerangka Aplikasi

Buka command line dan jalankan perintah berikut untuk meng-install Laravel 7

composer create-project --prefer-dist laravel/laravel komentar

Kemudian buat database, dan sesuaikan informasi database yang kamu miliki. Buka file .env dan modifikasi code berikut

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel-comment
DB_USERNAME=root
DB_PASSWORD=

Hapus semua file migration bawaan Laravel, dan generate migration baru dengan command

php artisan make:model Post -m

Buka file migration yang baru saja di-generate dan modifikasi menjadi

<?php

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

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

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

Eksekusi migration yang baru saja dibuat dengan command php artisan migrate. Adapun fungsi yang akan meng-handle data dummy dari postingan adalah dengan memanfaatkan model factory. Dari command line, jalankan perintah

php artisan make:factory PostFactory

Buka file PostFactory.php yang berada di dalam folder database/factories dan modifikasi menjadi

<?php

/** @var \Illuminate\Database\Eloquent\Factory $factory */

use App\Post;
use Faker\Generator as Faker;
use Illuminate\Support\Str;

$factory->define(Post::class, function (Faker $faker) {
    $title = $faker->paragraph($nbSentences = 3, $variableNbSentences = true);
    return [
        'title' => $title,
        'slug' => Str::slug($title),
        'content' => $faker->text($maxNbChars = 200)
    ];
});

Tugas selanjutnya adalah membuat seeder menjalankan model factory diatas, buat seeder baru dengan command

php artisan make:seeder PostTableSeeder 

Dari file PostTableSeeder.php yang baru saja di-generate, modifikasi menjadi

<?php

use Illuminate\Database\Seeder;

class PostTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        factory(App\Post::class, 50)->create();
    }
}

Sebelum meng-eksekusi seeder yang ada, buka file Post.php dan tambahkan guarded di dalamnya.

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    protected $guarded = [];
}

Kemudian eksekusi seeder di atas dengan command php artisan db:seed --class=PostTableSeeder. Secara otomatis, 50 data dummy sudah tersedia dan bisa digunakan sebagai list artikel yang nantinya akan dikaitkan dengan sistem komentar.

sistem komentar laravel

Membuat Fitur Komentar

Table selanjutnya yang akan dikerjakan adalah table comments untuk menampung semua komentar pengguna. Karena kita tidak membuat fitur authentication, maka setiap kali pengguna akan melakukan komentar, maka wajib meng-input username-nya.

Generate sebuah migration baru dengan command

php artisan make:model Comment -m

Melirik ke folder database/migrations, terdapat migration baru, buka file tersebut dan modifikasi menjadi

<?php

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

class CreateCommentsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('comments', function (Blueprint $table) {
            $table->id();
            $table->unsignedBigInteger('post_id');
            $table->unsignedBigInteger('parent_id')->nullable();
            $table->string('username');
            $table->text('comment');
            $table->timestamps();
        });
    }

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

Penjelasan: Perhatikan pada bagian parent_id, dimana field ini akan berisi id dari table comments atau dengan kata lain, dia akan berelasi dengan dirinya sendiri. parent_id hanya akan terisi, jika pengguna melakukan komentar pada komentar yang sudah ada.

Eksekusi migration di atas dengan command php artisan migrate. Dari sisi database sudah tersedia, saatnya kita untuk menyediakan fiturnya secara sistem. Generate controller baru dengan command

php artisan make:controller PostController

Buka file PostController.php dan tambahkan method berikut

public function index()
{
    $posts = Post::orderBy('created_at', 'DESC')->paginate(10);
    return view('welcome', compact('posts'));
}

Jangan lupa untuk menambahkan use statement di dalam file yang sama

use App\Post;

Kemudian buka file welcome.blade.php untuk menampilkan seluruh postingan yang ada di database berdasarkan hasil query di atas dan modifikasi menjadi.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Komentar Sistem</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
</head>
<body>
    <div class="container-fluid">
        <div class="row">
            <div class="col-md-12">
                <div class="card mt-3">
                    <div class="card-body">
                        <div class="row">
                            @foreach ($posts as $row)
                            <div class="col-md-6 mb-4">
                                <div class="card">
                                    <div class="card-body">
                                        <h5><a href="{{ url('/' . $row->slug) }}" style="text-decoration: none">{{ \Str::limit($row->title, 100) }}</a></h5>
                                        <p>{!! $row->content !!}</p>
                                    </div>
                                </div>
                            </div>
                            @endforeach
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</body>
</html>

Definisikan routing-nya dengan cara memodifikasi file routes/web.php menjadi

<?php

use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', 'PostController@index');

Ketika salah satu artikel diklik, maka akan mengarah ke halaman baru untuk menampilkan detail artikel. Tambahkan method berikut ke dalam file PostController.php

public function show($slug)
{
    $post = Post::with(['comments', 'comments.child'])->where('slug', $slug)->first();
    return view('show', compact('post'));
}

Sebelum membuat file baru, perhatikan pada eager loading with() dimana kita me-load relationships ke comments dan child yang berada di model Comment. Buka file Post.php dan tambahkan code berikut

public function comments()
{
    return $this->hasMany(Comment::class)->whereNull('parent_id');
}

Penjelasan: Membuat relasi ke table comments dengan kondisi hanya data yang parent_id-nya sama dengan NULL.

Relasi selanjutnya adalah child yang akan berelasi dengan dirinya sendiri, buka file Comment.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Comment extends Model
{
    protected $guarded = [];
    
    public function child()
    {
        return $this->hasMany(Comment::class, 'parent_id');
    }
}

Penjelasan: child berelasi dengan Comment atau dirinya sendiri dengan parent_id sebagai foreign key-nya.

Saatnya untuk membuat file show.blade.php untuk meng-handle tampilan komentar dan form komentar. Tambahkan code berikut

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>{{ \Str::limit($post->title, 50) }}</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
    <style>
        blockquote {
            background: #f9f9f9;
            border-left: 10px solid #ccc;
            margin: 1.5em 10px;
            padding: 0.5em 10px;
            quotes: "\201C""\201D""\2018""\2019";
        }
        blockquote:before {
            color: #ccc;
            content: open-quote;
            font-size: 4em;
            line-height: 0.1em;
            margin-right: 0.25em;
            vertical-align: -0.4em;
        }
        blockquote p {
            display: inline;
            font-style: italic;
        }
        blockquote h6 {
            font-weight: 700;
            padding: 0;
            margin: 0 0 .25rem;
        }
        .child-comment {
            padding-left: 50px;
        }
    </style>
</head>
<body>
    <div class="container-fluid">
        <div class="row">
            <div class="col-md-12">
                <div class="card mt-3">
                    <div class="card-body">
                        <h3>{{ $post->title }}</h3>
                        <p>{{ $post->content }}</p>
                        <hr>
                        <h5>Komentar</h5>
                        <div class="row">
                            <div class="col-md-6">
                                <form action="{{ url('/comment') }}" method="post">
                                    @csrf
                                    <input type="hidden" name="id" value="{{ $post->id }}" class="form-control">
                                    <input type="hidden" name="parent_id" id="parent_id" class="form-control">
                                    <div class="form-group">
                                        <label for="">Username</label>
                                        <input type="text" class="form-control" name="username">
                                        <p class="text-danger">{{ $errors->first('username') }}</p>
                                    </div>
                                    <div class="form-group" style="display: none" id="formReplyComment">
                                        <label for="">Balas Komentar</label>
                                        <input type="text" id="replyComment" class="form-control" readonly>
                                    </div>
                                    <div class="form-group">
                                        <label for="">Komentar</label>
                                        <textarea name="comment" cols="30" rows="10" class="form-control"></textarea>
                                    </div>
                                    <button class="btn btn-primary btn-sm">Kirim</button>
                                </form>
                            </div>
                            <div class="col-md-6">
                                @foreach ($post->comments as $row)
                                    <blockquote>
                                        <h6>{{ $row->username }}</h6>
                                        <hr>
                                        <p>{{ $row->comment }}</p><br>
                                        <a href="javascript:void(0)" onclick="balasKomentar({{ $row->id }}, '{{ $row->comment }}')">Balas</a>
                                    </blockquote>
                                    @foreach ($row->child as $val) 
                                        <div class="child-comment">
                                            <blockquote>
                                                <h6>{{ $val->username }}</h6>
                                                <hr>
                                                <p>{{ $val->comment }}</p><br>
                                            </blockquote>
                                        </div>
                                    @endforeach
                                @endforeach
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
    <script>
        function balasKomentar(id, title) {
            $('#formReplyComment').show();
            $('#parent_id').val(id)
            $('#replyComment').val(title)
        }
    </script>
</body>
</html>

Penjelasan: Kita memanfaatkan Javascript untuk mengisi input-an dengan name parent_id, jadi setiap kali tombol balas ditekan, maka secara otomatis akan mengisi data parent_id.

Lalu kita buat fungsi untuk meng-handle data komentar untuk disimpan ke database, buka file PostController.php dan tambahkan code

public function comment(Request $request)
{
    //VALIDASI DATA YANG DITERIMA
    $this->validate($request, [
        'username' => 'required',
        'comment' => 'required'
    ]);

    Comment::create([
        'post_id' => $request->id,
        //JIKA PARENT ID TIDAK KOSONG, MAKA AKAN DISIMPAN IDNYA, SELAIN ITU NULL
        'parent_id' => $request->parent_id != '' ? $request->parent_id:NULL,
        'username' => $request->username,
        'comment' => $request->comment
    ]);
    return redirect()->back()->with(['success' => 'Komentar Ditambahkan']);
}

Pastikan untuk menambahkan use statement di dalam file yang sama

use App\Comment;

Bagian terakhir adalah mendefinisikan routing-nya, buka file routes/web.php dan tambahkan code

Route::get('/{slug}', 'PostController@show');
Route::post('/comment', 'PostController@comment');

Baca Juga: Membuat Aplikasi Ekspedisi Lumen 6 #5: Manage Category

Kesimpulan

Membuat fitur sistem komentar menggunakan Laravel 7 atau PHP hanya memanfaatkan relasi data agar bisa menampilkan komentar yang saling terkait. Sehingga bagian yang perlu diperhatikan adalah struktur database agar kita bisa mengelola data komentar yang ada.

Dokumentasi code dari artikel ini bisa dilihat di Github.

Category:
Share:

Comments