Tutorial Portal Berita - Level 4: Manajemen Topik di Panel Admin πŸš€

Selamat datang di Level 4! Kali ini kita akan fokus pada Manajemen Topik di Panel Admin menggunakan Laravolt. Di level ini, kita akan menyaksikan kecepatan dalam membangun fungsionalitas CRUD (Create, Read, Update, Delete) dengan memanfaatkan code generator andalan, Thunderclap.

Kita akan membuat modul untuk Topik Berita secara otomatis, lalu mengintegrasikannya dengan sistem otorisasi yang telah kita siapkan di Level 3. Tujuannya adalah memastikan hanya pengguna yang berhak (Admin) yang dapat mengelola topik.

🎯 Tujuan Level 4:

  1. Menggunakan perintah laravolt:clap untuk membuat modul CRUD secara otomatis.
  2. Mendaftarkan modul yang baru dibuat ke dalam aplikasi Laravel.
  3. Mengintegrasikan otorisasi (Policies) ke dalam controller yang dihasilkan.
  4. Menambahkan menu baru ke sidebar Laravolt.
  5. Memverifikasi fungsionalitas CRUD dan keamanannya.

1. Persiapan: Model Topic dan Otorisasi

Sebelum menggunakan generator, pastikan model App\Models\Topic dan permissions terkait sudah siap dari level-level sebelumnya.

  • Model App\Models\Topic: Sudah dibuat di Level 2 dengan fillable attributes, traits (HasUlids, SoftDeletes, HasFactory), dan relasi yang diperlukan.
  • Permissions di App\Enums\Permission: Dari Level 3, kita sudah punya TOPIC_VIEW, TOPIC_CREATE, TOPIC_EDIT, dan TOPIC_DELETE.
  • Role Admin: Sudah memiliki semua permissions di atas.

2. Membuat TopicPolicy

Langkah ini sangat krusial untuk otorisasi. Jika belum dibuat di Level 3, sekarang saatnya. Pastikan juga sudah terdaftar di AuthServiceProvider.

1. Buat Policy

php artisan make:policy TopicPolicy --model=Topic

2. Isi app/Policies/TopicPolicy.php

<?php

namespace App\Policies;

use App\Enums\Permission;
use App\Models\Topic;
use App\Models\User;

class TopicPolicy
{
    public function before(User $user, string $ability): bool|null
    {
        if ($user->hasRole('admin')) {
            return true;
        }
        return null;
    }

    /**
     * Determine whether the user can view any models.
     */
    public function viewAny(User $user): bool
    {
        return $user->can(Permission::TOPIC_VIEW);
    }

    /**
     * Determine whether the user can view the model.
     */
    public function view(User $user, Topic $topic): bool
    {
        return $user->can(Permission::TOPIC_VIEW);
    }

    /**
     * Determine whether the user can create models.
     */
    public function create(User $user): bool
    {
        return $user->can(Permission::TOPIC_CREATE);
    }

    /**
     * Determine whether the user can update the model.
     */
    public function update(User $user, Topic $topic): bool
    {
        return $user->can(Permission::TOPIC_EDIT);
    }

    /**
     * Determine whether the user can delete the model.
     */
    public function delete(User $user, Topic $topic): bool
    {
        return $user->can(Permission::TOPIC_DELETE);
    }

    /**
     * Determine whether the user can restore the model.
     */
    public function restore(User $user, Topic $topic): bool
    {
        // Atau gunakan permission terpisah jika perlu
        return $user->can(Permission::TOPIC_EDIT);
    }

    /**
     * Determine whether the user can permanently delete the model.
     */
    public function forceDelete(User $user, Topic $topic): bool
    {
        // Biasanya hanya super admin (sudah di-handle oleh 'before')
        return false;
    }
}

3. Daftarkan di app/Providers/AuthServiceProvider.php

protected $policies = [
    // ... policies lain
    \App\Models\Topic::class => \App\Policies\TopicPolicy::class,
];

3. Implementasi AutoCRUD dengan Thunderclap (laravolt:clap) ⚑

Saatnya menggunakan sihir dari Thunderclap untuk membuat seluruh modul manajemen topik.

1. Jalankan Perintah Interaktif

Buka terminal dan jalankan perintah laravolt:clap:

php artisan laravolt:clap

2. Ikuti Proses Interaktif

  • Thunderclap akan meminta Anda memilih tabel dari database. Pilih topics.
  • Ikuti instruksi selanjutnya, dan generator akan membuat seluruh file yang dibutuhkan di dalam direktori modules/.

Struktur direktori baru yang akan terbentuk:

modules/
└── Topic/
    β”œβ”€β”€ Http/
    β”‚   β”œβ”€β”€ Controllers/
    β”‚   β”œβ”€β”€ Requests/
    β”‚   └── Tables/
    β”œβ”€β”€ Providers/
    β”œβ”€β”€ Models/ (Bisa dihapus nanti)
    β”œβ”€β”€ resources/
    β”‚   β”œβ”€β”€ lang/
    β”‚   └── views/
    └── routes/

Catatan: File yang dihasilkan mungkin berekstensi .stub. Laravolt biasanya akan otomatis mengubahnya menjadi .php.


4. Registrasi Modul Baru

Agar modul baru ini dikenali oleh aplikasi, kita perlu mendaftarkannya.

1. Perbarui composer.json

Tambahkan direktori modules/ ke dalam konfigurasi autoloading PSR-4.

"autoload": {
    "psr-4": {
        "App\\": "app/",
        "Database\\Factories\\": "database/factories/",
        "Database\\Seeders\\": "database/seeders/",
        "Modules\\": "modules/"
    }
},

2. Daftarkan Service Provider

Tambahkan Service Provider dari modul baru ke dalam bootstrap/providers.php.

<?php
return [
    // ... service provider lain
    \Modules\Topic\Providers\TopicServiceProvider::class,
];

3. Update Autoloader

Jalankan perintah berikut di terminal untuk memperbarui autoloader Composer.

composer dump-autoload

5. Kustomisasi dan Integrasi Otorisasi

Kode yang dihasilkan Thunderclap adalah fondasi. Sekarang, kita perlu menyesuaikannya.

1. Sesuaikan Controller

Buka modules/Topic/Http/Controllers/TopicController.php dan terapkan TopicPolicy.

  • Gunakan Model yang Benar: Pastikan controller menggunakan \App\Models\Topic.
  • Terapkan Otorisasi: Gunakan $this->authorize() di setiap action.
<?php

namespace Modules\Topic\Controllers;

use App\Models\Topic;
use Illuminate\Routing\Controller;
use Modules\Topic\Requests\Store;
use Modules\Topic\Requests\Update;
use Modules\Topic\Tables\TopicTableView;

class TopicController extends Controller
{
    public function index()
    {
        $this->authorize('viewAny', Topic::class);

        return TopicTableView::make()->view('topic::index');
    }

    public function create()
    {
        $this->authorize('create', Topic::class);

        return view('topic::create');
    }

    public function store(Store $request)
    {
        Topic::create($request->validated());

        return to_route('modules::topic.index')->withSuccess('Topic saved');
    }

    public function edit(Topic $topic)
    {
        $this->authorize('update', $topic);

        return view('topic::edit', compact('topic'));
    }

    public function update(Update $request, Topic $topic)
    {
        $topic->update($request->validated());

        return to_route('modules::topic.index')->withSuccess('Topic saved');
    }

    public function destroy(Topic $topic)
    {
        $this->authorize('delete', $topic);

        $topic->delete();

        return to_route('modules::topic.index')->withSuccess('Topic deleted');
    }
}

2. Hapus Model Duplikat

Karena kita sudah menggunakan App\Models\Topic, model di modules/Topic/Models/Topic.php bisa dihapus untuk menghindari kebingungan. Jangan lupa sesuaikan traits atau properties yang diperlukan di model utama jika ada yang belum ditambahkan.

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Concerns\HasUlids;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Laravolt\Suitable\AutoFilter;
use Laravolt\Suitable\AutoSearch;
use Laravolt\Suitable\AutoSort;

class Topic extends Model
{
    // baris lainnya tetap sama

    use AutoFilter, AutoSearch, AutoSort;

    protected $searchableColumns = ['name', 'slug', 'description'];

    // baris lainnya tetap sama
}

3. Sesuaikan TableView

Sama seperti Controller, kelas yang menampilkan data dalam bentuk tabel juga harus diarahkan untuk menggunakan model utama \App\Models\Topic.

Buka file TableView yang digenerate (misalnya modules/Topic/Tables/TopicTableView.php):

<?php

namespace Modules\Topic\Tables;

use App\Models\Topic;
use Laravolt\Suitable\Columns\Numbering;
use Laravolt\Suitable\Columns\RestfulButton;
use Laravolt\Suitable\Columns\Text;
use Laravolt\Suitable\TableView;

class TopicTableView extends TableView
{
    public function source()
    {
        return Topic::autoSort()->latest()->autoSearch(request('search'))->paginate();
    }

    protected function columns()
    {
        return [
            Numbering::make('No'),
            Text::make('name')->sortable(),
            Text::make('slug')->sortable(),
            Text::make('description')->sortable(),
            RestfulButton::make('modules::topic'),
        ];
    }
}

4. Sesuaikan Form Request

Pastikan form request (misal: Store.php dan Update.php) menggunakan permission yang benar.

Contoh modules/Topic/Requests/Store.php:

<?php

namespace Modules\Topic\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Str;

class Store extends FormRequest
{
    public function authorize()
    {
        return $this->user()->can('create', \App\Models\Topic::class);
    }

    protected function prepareForValidation()
    {
        if ($this->has('name') && !$this->has('slug')) {
            $this->merge(['slug' => Str::slug($this->input('name'))]);
        }
    }

    public function rules()
    {
        return [
            'name' => ['required', 'string', 'max:255'],
            'slug' => ['required', 'string', 'max:255', 'unique:topics,slug'],
            'description' => ['nullable', 'string', 'max:1000'],
        ];
    }
}

Contoh modules/Topic/Requests/Update.php:

<?php

namespace Modules\Topic\Requests;

class Update extends Store
{
    public function authorize()
    {
        // Model di-resolve oleh route model binding
        return $this->user()->can('update', $this->route('topic'));
    }

    public function rules()
    {
        $topicId = $this->route('topic')->id;
        return [
            'name' => ['required', 'string', 'max:255'],
            'slug' => ['required', 'string', 'max:255', 'unique:topics,slug,'.$topicId],
            'description' => ['nullable', 'string', 'max:1000'],
        ];
    }
}

5. Sesuaikan Route

Jika tidak memerlukan route show, kita bisa mengecualikannya di modules/Topic/routes/web.php.

Route::resource('topic', TopicController::class)->except('show');

6. Verifikasi Fungsionalitas dan Otorisasi βœ…

  1. Jalankan Aplikasi dan login sebagai Admin ([email protected]).
  2. Verifikasi Menu: Menu "Manajemen Topik" seharusnya muncul di sidebar.
  3. Test CRUD:
    • Klik menu tersebut dan Anda akan masuk ke halaman daftar topik.
    • Coba buat, edit, dan hapus topik. Pastikan semua berjalan lancar.
  4. Test Otorisasi:
    • Login sebagai Writer ([email protected]).
    • Pastikan menu "Manajemen Topik" tidak muncul.
    • Coba akses URL /admin/topics secara manual. Anda harus mendapatkan error 403 Forbidden.
    • Lakukan tes yang sama untuk role Member.

Selamat! πŸŽ‰

Anda telah berhasil menyelesaikan Level 4. Kini Anda mampu menggunakan Laravolt Thunderclap untuk membuat modul CRUD secara instan, mendaftarkannya, dan mengamankannya dengan sistem otorisasi berbasis policy.

Di Level 5, kita akan melangkah lebih jauh dengan Manajemen Post, yang melibatkan rich text editor dan unggah gambar. Sampai jumpa di level berikutnya!