Langsung ke konten

03. Membuat Chirp

Anda sekarang siap untuk mulai membangun aplikasi baru Anda! Mari kita izinkan pengguna kita untuk mem-posting pesan singkat yang disebut Chirp.

Model, migrasi, dan controller

Untuk memungkinkan pengguna mem-posting Chirp, kita perlu membuat model, migrasi, dan controller. Mari kita jelajahi masing-masing konsep ini sedikit lebih dalam:

  • Model menyediakan antarmuka yang handal dan menyenangkan bagi Anda untuk berinteraksi dengan tabel-tabel di database Anda.
  • Migrasi memungkinkan Anda untuk membuat dan memodifikasi tabel di database dengan mudah. Migrasi memastikan struktur database yang sama ada di mana pun aplikasi Anda dijalankan.
  • Controller bertanggung jawab untuk memproses permintaan yang masuk ke aplikasi Anda dan mengembalikan respons.

Hampir setiap fitur yang Anda bangun akan melibatkan semua bagian ini bekerja sama secara harmonis, sehingga perintah artisan make:model dapat membuat semuanya sekaligus untuk Anda.

Mari kita buat model, migrasi, dan resource controller untuk Chirp kita dengan perintah berikut:

php artisan make:model -mrc Chirp

Perintah ini akan membuat tiga file untuk Anda:

  • app/Models/Chirp.php - Model Eloquent.
  • database/migrations/<timestamp>_create_chirps_table.php - Migrasi database yang akan membuat tabel database milik Anda.
  • app/Http/Controllers/ChirpController.php - Controller HTTP yang akan menerima permintaan masuk dan mengembalikan respons.

Perutean (Routing)

Kita juga perlu membuat URL untuk controller kita. Kita bisa melakukan ini dengan menambahkan "route", yang dikelola di direktori routes pada proyek Anda. Karena kita menggunakan resource controller, kita dapat menggunakan satu pernyataan Route::resource() untuk mendefinisikan semua rute mengikuti struktur URL konvensional.

Untuk memulai, kita akan mengaktifkan dua rute:

  • Rute index akan menampilkan formulir dan daftar Chirp.
  • Rute store akan digunakan untuk menyimpan Chirp baru.

Kita juga akan menempatkan rute-rute ini di belakang dua middleware:

  • Middleware auth memastikan bahwa hanya pengguna yang sudah login yang dapat mengakses rute tersebut.
  • Middleware verified akan digunakan jika Anda memutuskan untuk mengaktifkan verifikasi email.
routes/web.php
<?php
 
+use App\Http\Controllers\ChirpController;
use App\Http\Controllers\ProfileController;
use Illuminate\Foundation\Application;
use Illuminate\Support\Facades\Route;
use Inertia\Inertia;
 
Route::get('/', function () {
return Inertia::render('Welcome', [
'canLogin' => Route::has('login'),
'canRegister' => Route::has('register'),
'laravelVersion' => Application::VERSION,
'phpVersion' => PHP_VERSION,
]);
});
 
Route::get('/dashboard', function () {
return Inertia::render('Dashboard');
})->middleware(['auth', 'verified'])->name('dashboard');
 
Route::middleware('auth')->group(function () {
Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
});
 
+Route::resource('chirps', ChirpController::class)
+ ->only(['index', 'store'])
+ ->middleware(['auth', 'verified']);
 
require __DIR__.'/auth.php';

Ini akan membuat rute-rute berikut:

Verb URI Action Route Name
GET /chirps index chirps.index
POST /chirps store chirps.store

Mari kita uji rute dan controller kita dengan mengembalikan pesan uji dari metode index di kelas ChirpController yang baru:

app/Http/Controllers/ChirpController.php
<?php
 ...
namespace App\Http\Controllers;
 
use App\Models\Chirp;
use Illuminate\Http\Request;
+use Illuminate\Http\Response;
 
class ChirpController extends Controller
{
/**
* Menampilkan daftar resource.
*/
- public function index()
+ public function index(): Response
{
- //
+ return response('Halo, Dunia!');
}
 ...
/**
* Menampilkan formulir untuk membuat resource baru.
*/
public function create()
{
//
}
 
/**
* Menyimpan resource yang baru dibuat ke dalam penyimpanan.
*/
public function store(Request $request)
{
//
}
 
/**
* Menampilkan resource yang dipilih/spesifik.
*/
public function show(Chirp $chirp)
{
//
}
 
/**
* Menampilkan formulir untuk mengedit resource yang dipilih/spesifik.
*/
public function edit(Chirp $chirp)
{
//
}
 
/**
* Memperbarui resource yang dipilih/spesifik di dalam penyimpanan.
*/
public function update(Request $request, Chirp $chirp)
{
//
}
 
/**
* Menghapus resource yang dipilih/spesifik dari penyimpanan.
*/
public function destroy(Chirp $chirp)
{
//
}
 
}

Jika Anda masih dalam kondisi login, Anda seharusnya melihat pesan tersebut saat mengunjungi http://localhost:8000/chirps, atau http://localhost/chirps jika Anda menggunakan Sail!

Inertia

Belum terkesan juga? Mari kita perbarui metode index dari kelas ChirpController kita untuk me-render komponen halaman front-end menggunakan Inertia. Inertia adalah penghubung antara aplikasi Laravel kita dengan front-end Vue atau React:

app/Http/Controllers/ChirpController.php
<?php
 
namespace App\Http\Controllers;
 
use App\Models\Chirp;
use Illuminate\Http\Request;
-use Illuminate\Http\Response;
+use Inertia\Inertia;
+use Inertia\Response;
 
class ChirpController extends Controller
{
/**
* Menampilkan daftar resource.
*/
public function index(): Response
{
- return response('Halo, Dunia!');
+ return Inertia::render('Chirps/Index', [
+ //
+ ]);
}
 ...
/**
* Menampilkan formulir untuk membuat resource baru.
*/
public function create()
{
//
}
 
/**
* Menyimpan resource yang baru dibuat ke dalam penyimpanan.
*/
public function store(Request $request)
{
//
}
 
/**
* Menampilkan resource yang dipilih/spesifik.
*/
public function show(Chirp $chirp)
{
//
}
 
/**
* Menampilkan formulir untuk mengedit resource yang dipilih/spesifik.
*/
public function edit(Chirp $chirp)
{
//
}
 
/**
* Memperbarui resource yang dipilih/spesifik di dalam penyimpanan.
*/
public function update(Request $request, Chirp $chirp)
{
//
}
 
/**
* Menghapus resource yang dipilih/spesifik dari penyimpanan.
*/
public function destroy(Chirp $chirp)
{
//
}
 
}

Selanjutnya, kita dapat membuat komponen halaman front-end Chirps/Index dengan formulir untuk membuat Chirp baru:

resources/js/Pages/Chirps/Index.vue
<script setup>
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout.vue';
import InputError from '@/Components/InputError.vue';
import PrimaryButton from '@/Components/PrimaryButton.vue';
import { useForm, Head } from '@inertiajs/vue3';
 
const form = useForm({
message: '',
});
</script>
 
<template>
<Head title="Chirps" />
 
<AuthenticatedLayout>
<div class="max-w-2xl mx-auto p-4 sm:p-6 lg:p-8">
<form @submit.prevent="form.post(route('chirps.store'), { onSuccess: () => form.reset() })">
<textarea
v-model="form.message"
placeholder="What's on your mind?"
class="block w-full border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 rounded-md shadow-sm"
></textarea>
<InputError :message="form.errors.message" class="mt-2" />
<PrimaryButton class="mt-4">Chirp</PrimaryButton>
</form>
</div>
</AuthenticatedLayout>
</template>
resources/js/Pages/Chirps/Index.jsx
import React from 'react';
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout';
import InputError from '@/Components/InputError';
import PrimaryButton from '@/Components/PrimaryButton';
import { useForm, Head } from '@inertiajs/react';
 
export default function Index({ auth }) {
const { data, setData, post, processing, reset, errors } = useForm({
message: '',
});
 
const submit = (e) => {
e.preventDefault();
post(route('chirps.store'), { onSuccess: () => reset() });
};
 
return (
<AuthenticatedLayout>
<Head title="Chirps" />
 
<div className="max-w-2xl mx-auto p-4 sm:p-6 lg:p-8">
<form onSubmit={submit}>
<textarea
value={data.message}
placeholder="What's on your mind?"
className="block w-full border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 rounded-md shadow-sm"
onChange={e => setData('message', e.target.value)}
></textarea>
<InputError message={errors.message} className="mt-2" />
<PrimaryButton className="mt-4" disabled={processing}>Chirp</PrimaryButton>
</form>
</div>
</AuthenticatedLayout>
);
}

Selesai! Segarkan halaman di peramban Anda untuk melihat formulir baru Anda di-render dalam layout default yang disediakan oleh Breeze!

Formulir Chirp

Sekarang karena front-end kita telah ditenagai oleh JavaScript, setiap perubahan yang kita buat pada template JavaScript kita akan dimuat ulang secara otomatis di peramban setiap kali server pengembangan Vite berjalan melalui npm run dev.

Menu navigasi

Mari luangkan waktu sejenak untuk menambahkan tautan ke menu navigasi yang disediakan oleh Breeze.

Perbarui komponen AuthenticatedLayout yang disediakan oleh Breeze untuk menambahkan item menu untuk layar desktop:

resources/js/Layouts/AuthenticatedLayout.vue
<div class="hidden space-x-8 sm:-my-px sm:ml-10 sm:flex">
<NavLink :href="route('dashboard')" :active="route().current('dashboard')">
Dashboard
</NavLink>
+ <NavLink :href="route('chirps.index')" :active="route().current('chirps.index')">
+ Chirps
+ </NavLink>
</div>
resources/js/Layouts/AuthenticatedLayout.jsx
<div className="hidden space-x-8 sm:-my-px sm:ml-10 sm:flex">
<NavLink href={route('dashboard')} active={route().current('dashboard')}>
Dashboard
</NavLink>
+ <NavLink href={route('chirps.index')} active={route().current('chirps.index')}>
+ Chirps
+ </NavLink>
</div>

Dan juga untuk layar mobile:

resources/js/Layouts/AuthenticatedLayout.vue
<div class="pt-2 pb-3 space-y-1">
<ResponsiveNavLink :href="route('dashboard')" :active="route().current('dashboard')">
Dashboard
</ResponsiveNavLink>
+ <ResponsiveNavLink :href="route('chirps.index')" :active="route().current('chirps.index')">
+ Chirps
+ </ResponsiveNavLink>
</div>
resources/js/Layouts/AuthenticatedLayout.jsx
<div className="pt-2 pb-3 space-y-1">
<ResponsiveNavLink href={route('dashboard')} active={route().current('dashboard')}>
Dashboard
</ResponsiveNavLink>
+ <ResponsiveNavLink href={route('chirps.index')} active={route().current('chirps.index')}>
+ Chirps
+ </ResponsiveNavLink>
</div>

Menyimpan Chirp

Formulir kita telah dikonfigurasi untuk mem-posting pesan ke rute chirps.store yang kita buat tadi. Mari kita perbarui metode store pada kelas ChirpController kita untuk memvalidasi data dan membuat Chirp baru:

app/Http/Controllers/ChirpController.php
<?php
 
namespace App\Http\Controllers;
 
use App\Models\Chirp;
+use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Inertia\Inertia;
use Inertia\Response;
 
class ChirpController extends Controller
{
 ...
/**
* Menampilkan daftar resource.
*/
public function index(): Response
{
return Inertia::render('Chirps/Index', [
//
]);
}
 
/**
* Menampilkan formulir untuk membuat resource baru.
*/
public function create()
{
//
}
 
/**
* Menyimpan resource yang baru dibuat ke dalam penyimpanan.
*/
- public function store(Request $request)
+ public function store(Request $request): RedirectResponse
{
- //
+ $validated = $request->validate([
+ 'message' => 'required|string|max:255',
+ ]);
+ 
+ $request->user()->chirps()->create($validated);
+ 
+ return redirect(route('chirps.index'));
}
 ...
/**
* Menampilkan resource yang dipilih/spesifik.
*/
public function show(Chirp $chirp)
{
//
}
 
/**
* Menampilkan formulir untuk mengedit resource yang dipilih/spesifik.
*/
public function edit(Chirp $chirp)
{
//
}
 
/**
* Memperbarui resource yang dipilih/spesifik di dalam penyimpanan.
*/
public function update(Request $request, Chirp $chirp)
{
//
}
 
/**
* Menghapus resource yang dipilih/spesifik dari penyimpanan.
*/
public function destroy(Chirp $chirp)
{
//
}
 
}

Kita menggunakan fitur validasi Laravel yang andal untuk memastikan bahwa pengguna memberikan pesan dan pesan tersebut tidak melebihi batas 255 karakter dari kolom database yang akan kita buat.

Kita kemudian membuat catatan yang akan dimiliki oleh pengguna yang sedang login dengan memanfaatkan relasi chirps. Kita akan mendefinisikan relasi tersebut segera.

Terakhir, kita mengembalikan respons redirect untuk mengirim pengguna kembali ke rute chirps.index.

Membuat relasi

Anda mungkin memperhatikan pada langkah sebelumnya bahwa kita memanggil metode chirps pada objek $request->user(). Kita perlu membuat metode ini pada model User kita untuk mendefinisikan relasi "has many":

app/Models/User.php
<?php
 ...
namespace App\Models;
 
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
 
class User extends Authenticatable
{
 ...
use HasApiTokens, HasFactory, Notifiable;
 
/**
* Atribut-atribut yang dapat di-assign secara massal.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
];
 
/**
* Atribut-atribut yang harus disembunyikan untuk serialisasi.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
 
/**
* Atribut-atribut yang perlu diubah tipenya (casting).
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
 
+ public function chirps(): HasMany
+ {
+ return $this->hasMany(Chirp::class);
+ }
}

Perlindungan mass assignment

Melewatkan/mengoper semua data dari request ke model Anda bisa berisiko. Bayangkan Anda memiliki halaman di mana pengguna dapat mengedit profil mereka. Jika Anda mengoper seluruh request ke model, maka pengguna dapat mengedit kolom apa pun yang mereka suka, seperti kolom is_admin. Ini disebut kerentanan mass assignment.

Laravel melindungi Anda dari ketidaksengajaan ini dengan memblokir mass assignment secara default. Walau begitu, mass assignment tetap nyaman, karena mencegah Anda menetapkan setiap atribut satu per satu. Kita dapat mengaktifkan mass assignment untuk atribut yang aman dengan menandainya sebagai "fillable".

Mari tambahkan properti $fillable ke model Chirp kita untuk mengaktifkan mass-assignment untuk atribut message:

app/Models/Chirp.php
<?php
 ...
namespace App\Models;
 
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
 
class Chirp extends Model
{
 ...
use HasFactory;
 
+ protected $fillable = [
+ 'message',
+ ];
}

Anda dapat mempelajari lebih lanjut tentang perlindungan mass assignment milik Laravel di dokumentasi.

Memperbarui migrasi

Selama pembuatan aplikasi, Laravel sudah menerapkan migrasi default yang disertakan dalam direktori database/migrations. Anda dapat memeriksa struktur database saat ini dengan menggunakan perintah php artisan db:show dan php artisan db:table:

php artisan db:show
php artisan db:table users

Jadi, satu-satunya yang kurang adalah kolom tambahan di database kita untuk menyimpan hubungan antara Chirp dan User-nya serta pesan itu sendiri. Ingat migrasi database yang kita buat sebelumnya? Saatnya membuka file tersebut untuk menambahkan beberapa kolom tambahan:

database/migrations/<timestamp>_create_chirps_table.php
<?php
 ...
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
 
return new class extends Migration
{
/**
* Menjalankan migrasi.
*/
public function up(): void
{
Schema::create('chirps', function (Blueprint $table) {
$table->id();
+ $table->foreignId('user_id')->constrained()->cascadeOnDelete();
+ $table->string('message');
$table->timestamps();
});
}
 ...
/**
* Mengembalikan migrasi.
*/
public function down(): void
{
Schema::dropIfExists('chirps');
}
 
};

Kita belum memigrasi database sejak kita menambahkan migrasi ini, jadi mari kita lakukan sekarang:

php artisan migrate

Coba hasilnya

Kita sekarang siap untuk mengirim Chirp menggunakan formulir yang baru saja kita buat! Kita belum akan bisa melihat hasilnya karena kita belum menampilkan Chirp yang ada di halaman tersebut.

Formulir Chirp

Jika Anda mengosongkan kolom pesan, atau memasukkan lebih dari 255 karakter, maka Anda akan melihat validasi bekerja.

Artisan Tinker

Ini adalah waktu yang tepat untuk mempelajari tentang Artisan Tinker, sebuah REPL (Read-eval-print loop) di mana Anda dapat mengeksekusi kode PHP apa pun di aplikasi Laravel Anda.

Di konsol Anda, mulai sesi tinker baru:

php artisan tinker

Selanjutnya, eksekusi kode berikut untuk menampilkan Chirps di database Anda:

App\Models\Chirp::all();
=> Illuminate\Database\Eloquent\Collection {#4512
all: [
App\Models\Chirp {#4514
id: 1,
user_id: 1,
message: "Aku membangun Chirper dengan Laravel!",
created_at: "2022-08-24 13:37:00",
updated_at: "2022-08-24 13:37:00",
},
],
}

Anda dapat keluar dari Tinker dengan menggunakan perintah exit, atau dengan menekan Ctrl + c.

Lanjutkan untuk mulai menampilkan Chirp...