Tutorial Laravel 12 – Membuat Website Blog Berita – Bagian #3 (Relasi Kategori dan Tag Berita)

3. Menambahkan Fitur Kategori & Tag pada Berita (Laravel)

Pada tahap ini, kita akan menambahkan kategori (one-to-many) dan tag (many-to-many) pada fitur berita (posts), sehingga konten menjadi lebih terorganisir dan profesional seperti portal berita sungguhan.


Tujuan Pembelajaran

Setelah menyelesaikan bagian ini, peserta mampu:

  • Membuat tabel categories dan tags
  • Menghubungkan Post ↔ Category ↔ Tag
  • Menampilkan kategori & tag di halaman berita
  • Mengelola kategori dan tag saat tambah & edit berita

A. Membuat Model & Migration

1. Generate Model dan Migration

php artisan make:model Category -m
php artisan make:model Tag -m

2. Migration Tabel Categories

Edit file create_categories_table.php:

Schema::create('categories', function (Blueprint $table) {
  $table->id();
  $table->string('name');
  $table->timestamps();
});

3. Migration Tabel Tags

Edit file create_tags_table.php:

Schema::create('tags', function (Blueprint $table) {
  $table->id();
  $table->string('name');
  $table->timestamps();
});

B. Menambahkan Relasi ke Posts

1. Buat Migration Relasi

php artisan make:migration add_category_id_to_posts_table --table=posts
php artisan make:migration create_post_tag_table --create=post_tag

2. Tambahkan Category ke Posts

File add_category_id_to_posts_table.php:

Schema::table('posts', function (Blueprint $table) {
  $table->foreignId('category_id')->nullable()->constrained('categories')->onDelete('set null');
});

3. Tabel Pivot Post–Tag

File create_post_tag_table.php:

Schema::create('post_tag', function (Blueprint $table) {
  $table->id();
  $table->foreignId('post_id')->constrained()->onDelete('cascade');
  $table->foreignId('tag_id')->constrained()->onDelete('cascade');
});

4. Jalankan Migration

php artisan migrate

C. Menambahkan Relasi di Model

1. Model Post : Post.php

public function category()
{
  return $this->belongsTo(Category::class);
}

public function tags()
{
  return $this->belongsToMany(Tag::class);
}

2. Model Category : Category.php

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Category extends Model
{
  use HasFactory;
  protected $fillable = [
      'name',
  ];
  // Relasi: satu kategori punya banyak post
  public function posts()
  {
      return $this->hasMany(Post::class);
  }
}

Model Tag : Tag.php

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Tag extends Model
{
  use HasFactory;
  protected $fillable = [
      'name',
  ];
  // Relasi: satu tag bisa dimiliki banyak post
  public function posts()
  {
      return $this->belongsToMany(Post::class);
  }
}

D. Ubah Form Tambah Berita

1. Dropdown Kategori

Tambahkan dropdown kategori dan checkbox tag.

<div class="mb-3">
  <label>Kategori</label>
  <select name="category_id" class="form-control">
      <option value="">-- Pilih Kategori --</option>
      @foreach($categories as $category)
          <option value="{{ $category->id }}">{{ $category->name }}</option>
      @endforeach
  </select>
</div>

<div class="mb-3">
  <label>Tag</label><br>
  @foreach($tags as $tag)
      <label><input type="checkbox" name="tags[]" value="{{ $tag->id }}"> {{ $tag->name }}</label><br>
  @endforeach
</div>

E. Update PostController

1. Import Model

Lalu di PostController, tambahkan:

<?php
namespace App\Http\Controllers;
use App\Models\Post;
use App\Models\Category; // Jika perlu kategori
use App\Models\Tag;
use Illuminate\Http\Request;

2. Method create()

Lalu pada create() di PostController, tambahkan:

    public function create()
  {
      $categories = Category::orderBy('name')->get();
      $tags = Tag::orderBy('name')->get();
      // cara lain jika juga butuh tags:
      //$tags = \App\Models\Tag::orderBy('name')->get();
      return view('posts.create', compact('categories','tags'));
    }

3. Method store()

Lalu pada store() di PostController, tambahkan:

public function store(Request $request)
  {
      // 1. Validasi input
      $request->validate([
          'title'       => 'required|string|max:255',
          'content'     => 'required|string',
          'author'      => 'nullable|string|max:100',
          'category_id' => 'nullable|exists:categories,id',
          'tags'        => 'nullable|array',
          'tags.*'      => 'exists:tags,id',
          'thumbnail'   => 'nullable|image|mimes:jpeg,png,jpg|max:2048',
      ]);
      // 2. Ambil data utama
      $data = $request->only(['title', 'content', 'author', 'category_id']);
      // 3. Upload thumbnail jika ada
      if ($request->hasFile('thumbnail')) {
          $path = $request->file('thumbnail')->store('thumbnails', 'public');
          $data['thumbnail'] = $path;
      }
      // 4. Simpan post baru
      $post = \App\Models\Post::create($data);
      // 5. Simpan relasi tags ke tabel pivot
      if ($request->has('tags')) {
          $post->tags()->sync($request->tags);
      }
      // 6. Redirect dengan pesan sukses
      return redirect()
          ->route('posts.index')
          ->with('success', 'Berita berhasil ditambahkan!');
  }

F. Halaman Detail / Show Berita

Ubah script di edit.blade.php, menjadi :

@extends('layouts.app')
@section('content')
<div class="container">
  <div class="card shadow-sm mb-4">
      {{-- Thumbnail --}}
      @if($post->thumbnail)
          <img src="{{ asset('storage/' . $post->thumbnail) }}"
               alt="{{ $post->title }}"
               class="card-img-top"
               style="max-height: 400px; object-fit: cover;">
        @endif

      <div class="card-body">
          {{-- Judul --}}
          <h2 class="card-title">{{ $post->title }}</h2>
          {{-- Info tambahan --}}
          <div class="text-muted mb-3">
              <small>
                  Oleh <strong>{{ $post->author ?? 'Admin' }}</strong> |
                  {{ $post->created_at->format('d M Y') }}
              </small><br>
              {{-- Kategori --}}
              @if($post->category)
                  <span class="badge bg-primary">Kategori: {{ $post->category->name }}</span>
              @endif
          </div>
          {{-- Isi berita --}}
          <div class="card-text" style="white-space: pre-line;">
              {!! nl2br(e($post->content)) !!}
          </div>
          {{-- Tag --}}
          @if($post->tags->count() > 0)
              <div class="mt-4">
                  <strong>Tags:</strong><br>
                  @foreach($post->tags as $tag)
                      <span class="badge bg-secondary">{{ $tag->name }}</span>
                  @endforeach
              </div>
          @endif
          {{-- Tombol aksi --}}
          <div class="mt-4">
              <a href="{{ route('posts.index') }}" class="btn btn-outline-secondary">Kembali</a>
          </div>
      </div>
  </div>
</div>
@endsection

G. Edit & Update Berita

Ubah function edit() dan update() di PostController.php :

public function edit($id)
  {
      // Ambil data post dengan relasi category dan tags
      $post = Post::with(['category', 'tags'])->findOrFail($id);
      // Ambil semua kategori dan tags untuk form
      $categories = Category::all();
      $tags = Tag::all();
      return view('posts.edit', compact('post', 'categories', 'tags'));
  }
//—-----------------------
public function update(Request $request, $id)
  {
      // 1. Validasi input
      $request->validate([
          'title' => 'required|string|max:255',
          'content' => 'required|string',
          'author' => 'nullable|string|max:100',
          'category_id' => 'nullable|exists:categories,id',
          'tags' => 'nullable|array',
          'tags.*' => 'exists:tags,id',
          'thumbnail' => 'nullable|image|mimes:jpeg,png,jpg|max:2048',
      ]);
      // 2. Temukan post
      $post = Post::findOrFail($id);
      // 3. Ambil data utama
      $data = $request->only(['title', 'content', 'author', 'category_id']);
      // 4. Jika upload thumbnail baru
      if ($request->hasFile('thumbnail')) {
          // Hapus file lama (jika ada)
          if ($post->thumbnail && \Storage::disk('public')->exists($post->thumbnail)) {
              \Storage::disk('public')->delete($post->thumbnail);
          }
          // Upload file baru
          $path = $request->file('thumbnail')->store('thumbnails', 'public');
          $data['thumbnail'] = $path;
      }
      // 5. Update data post
      $post->update($data);
      // 6. Update relasi tags
      if ($request->has('tags')) {
          $post->tags()->sync($request->tags);
      } else {
          $post->tags()->detach(); // kalau tidak ada tag, kosongkan
      }
      // 7. Redirect dengan pesan sukses
      return redirect()
          ->route('posts.index')
          ->with('success', 'Berita berhasil diperbarui!');
  }
Tags :

Leave a Reply

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

11 − 6 =

Tutorial Terpopuler

Tutorial Pilihan

Tentang Gurututorku

Gurututorku adalah sebuah kursus online maupun offline yang menyediakan berbagai macam tutorial tentang pemrograman, design, dan multimedia serta artikel teknologi yang tentunya bisa dinikmati dan dipelajari oleh semua orang yang baru ingin belajar dunia IT.

© 2024 – Gurututorku.com | Learning today success tomorrow.