3. Design Pattern, Database Migration, dan CRUD pada Laravel - Algoritma-dan-Pemrograman-ITS/camin-2023 GitHub Wiki
Buatlah sebuah project Laravel baru dengan nama camin-2023
Design pattern sendiri adalah suatu metode yang digunakan untuk membantu pengembang menemukan solusi dari suatu masalah-masalah umum yang muncul saat pengembangan perangkat lunak sedang berlangsung. Selain itu, pola desain ini dapat digunakan berulang kali dan dapat mempercepat proses pengembangan perangkat lunak. Sebenarnya, design patterns memiliki banyak jenis yang berbeda-beda. Namun, penulis hanya akan membatasinya menjadi:
Service layer merupakan suatu design pattern yang bertujuan untuk mengurangi kompleksitas pada controller. Service layer akan mengabstraksi logika-logika bisnis yang sebelumnya ada di controller, sehingga controller hanya bertanggung jawab dalam menerima request, mengirim request tersebut untuk diproses oleh service layer, dan mengembalikan sebuah response berdasarkan hasil olahan dari service layer. Service layer dapat diterapkan pada sebuah controller dengan menggunakan dependency injection. Penjelasan beserta implementasi dari service layer dapat ditonton pada link berikut https://www.youtube.com/watch?v=8DBMRByJUmU.
Repository layer merupakan sebuah perantara antara controller dan model. Repository akan menyimpan logika-logika pengambilan data pada controller, dimana data tersebut diambil dari model. Beberapa fungsi layer ini adalah seperti manipulasi output dari model dan remapping data (mengubah penamaan kolom, dll). Penerapan Repository Layer ini juga sama seperti service layer, yaitu dengan menerapkan depedency injection. Penjelasan beserta implementasi dari repository layer dapat ditonton pada link berikut https://www.youtube.com/watch?v=giJcdfW2wC8.
Adapun kini beberapa developer menggabungkan service layer pattern dengan repository layer pattern dengan implementasi seperti:
Contracts adalah sebuah panggilan lain untuk interface yang populer dikalangan pengembang Laravel. Penamaan contract diambil dari sifat interface yang menyerupai sebuah kontrak bagi kelas-kelas yang mengimplementasikannya.
Facade merupakan design pattern yang menyediakan fungsi pseudo-static dengan menyembunyikan instansiasi kelas dari fungsi tersebut. Fungsi tersebut dapat diakses secara langsung dapat menginstansiasi kelasnya terlebih dahulu, meskipun fungsi tersebut tidak bersifat statis. Contoh dari penggunaan facade bisa dilihat pada kode dibawah ini:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Cache;
class UserController extends Controller
{
/**
* Show the profile for the given user.
*
* @param int $id
* @return Response
*/
public function showProfile($id)
{
$user = Cache::get('user:'.$id);
return view('profile', ['user' => $user]);
}
}
Facade pada potongan kode tersebut terdapat pada kelas Cache
. Pada baris Cache::get(...)
, fungsi get()
pada kelas Cache
tersebut dipanggil secara statis. Implementasi dari facade bisa dilihat di video berikut https://www.youtube.com/watch?v=zR6JnwH7MSQ.
Sebelum memulai mengenai materi, yang pertama harus dilakukan yaitu menghubungkan project Laravel yang kita miliki dengan database. Untuk project ini yang kita gunakan yaitu MySQL dan PhpMyAdmin yang sudah tersedia pada XAMPP.
Buka file .env
dan ubah value DB_DATABASE
sesuai dengan nama database. Untuk contoh kali ini, ubahlah menjadi camin-2023
Migration merupakan salah satu fitur Laravel yang berfungsi seperti version control
untuk database. Migration merupakan suatu metode yang bisa digunakan untuk membuat skema database kita secara langsung dengan mengeksekusi kode program, bukan melalui eksekusi query SQL. Dengan menggunakan migration, kita menggantikan query SQL dengan kode program PHP untuk membuat skema database kita.
Kita dapat melihat isi dari file yang didapatkan setelah melakukan init project. Contoh coba kita lihat di /database/migrations/create_users_table.php
. Di dalam file migration terdapat 2 function yakni :
Function up()
: Berisi perubahan struktur database yang kita inginkan.
Function down()
: Berisi pembatalan aksi terhadap perubahan struktur yang dilakukan oleh function up()
.
Migration dikarenakan seperti menjadi control version system, semua perubahan database yang kita lakukan dengan migration semuanya di data, sehingga kita bisa melakukan rollback dengan mudah saat kita ingin mengubah format table ke versi sebelumnya sebelum kita ubah.
php artisan migrate:rollback
Perintah migrate:refresh akan mengembalikan semua migrasi kita dan kemudian menjalankan perintah migrasi. Perintah ini secara efektif membuat ulang seluruh database yang kita miliki.
php artisan migrate:refresh
Apabila kita ingin mengulang dari awal/melakukan drop pada semua tabel dan melakukan migrasi kembali, dapat menggunakan perintah di bawah ini.
php artisan migrate:fresh
Apabila kita ingin membuat migrasi baru, maka kita dapat menggunakan perintah php artisan make:migration
pada terminal dan mendefinisikan tabel yang ingin kita buat. Misalkan pada project, kita akan membuat tabel untuk data-data Camin.
php artisan make:migration create_camins_table
Apabila sudah, kemudian kita dapat membuka file yang terletak pada /database/migrations
. Pada fungsi up()
dapat kita sesuaikan dengan yang sebelumnya kita punya.
public function up()
{
Schema::create('camins', function (Blueprint $table) {
$table->id();
$table->string('nama')->unique();
$table->string('nrp')->unique(); // karena identifier
$table->string('jurusan');
$table->string('angkatan');
$table->timestamps();
});
}
Untuk mengetahui tipe data yang dapat digunakan dapat melihat di sini.
Kemudian lakukan php artisan migrate
. Apabila database tidak ditemukan, maka kita akan mendapatkan opsi untuk membuat database sekaligus melakukan migrasi.
Apabila kita sudah melakukan migrasi sebelumnya, kita dapat gunakan php artisan migrate:fresh
.
Laravel menyertakan Eloquent, sebuah object-relational mapper (ORM) yang membuatnya mudah untuk berinteraksi dengan database. Saat menggunakan Eloquent, setiap tabel database memiliki Model
terkait yang digunakan untuk berinteraksi dengan tabel tersebut. Selain mengambil record dari tabel database, model Eloquent juga memungkinkan Anda untuk menambah, mengupdate, dan menghapus record dari tabel.
Buatlah sebuah model bernama Angkatan
beserta controller dan migrasinya
php artisan make:model Angkatans -mcr
Note: Jangan lupa juga untuk membuat model dan controller dari Camin
Isi atribut tabel di file migrasi Angkatan
Schema::create('angkatans', function (Blueprint $table) {
$table->id();
$table->string('nama')->unique();
$table->string('slug')->unique(); // karena identifier
$table->timestamps();
});
Pada tabel Camin, buatlah sebuah foreign key (foreign id) yang mereferensikan id dari Angkatan dengan membuat file migrasi baru Camin dengan mengganti:
$table->string('angkatan');
dengan
$table->foreignId('angkatan_id')->constrained();
Dalam model Angkatan, isi dengan:
class Angkatan extends Model
{
use HasFactory;
protected $table = "angkatans";
protected $fillable = ["nama", "slug"];
}
di mana protected $table
diassign dengan string nama tabel pada database, dan protected $fillable
diassign dengan kolom tabel tersebut.
Note: Lakukan hal yang sama pada model Camin
Kemudian, lakukan migrasi ulang.
Menentukan relasi antar tabel dapat dilakukan pada masing-masing model. Dari case yang kita angkat, maka dapat diketahui:
- 1 camin memiliki 1 angkatan
- 1 angkatan memiliki banyak camin
Perlu diketahui bahwa pada penggunaan Eloquent di Laravel, kita dapat membuat beberapa relation sebagai berikut:
- hasOne
- hasMany
- belongsTo
- belongsToMany
Perlu diperhatikan bahwa belongsTo
dan belongsToMany
merupakan inverse dari hasOne
dan hasMany
, yang mana program akan mencocokkan foreign key model belongsTo
ke primary key (id) dari model has
Maka, dapat kita simpulkan:
- 1 camin belongsTo 1 angkatan
- 1 angkatan hasMany camin
Maka dari itu, buatlah sebuah function pada model Camin:
public function angkatan(){
return $this->belongsTo(Angkatan::class);
}
dan pada model Angkatan:
public function camin(){
return $this->hasMany(Camin::class);
}
Gunakan Tinker:
php artisan tinker
untuk create record baru Angkatan dan Camin:
Angkatan::create([
'nama' => '2020',
'slug' => '2020'
]);
Camin::create([
'nama' => 'Bagas D',
'angkatan_id' => 1,
'nrp' => '5025201096',
'jurusan' => 'Teknik Informatika',
]);
Note: apabila terjadi error class not found
, run composer dump-autoload
Kemudian cek hasil relasi kedua record.
Dalam pemrograman komputer, Create, Read, Update, dan Delete (CRUD) adalah empat operasi dasar penyimpanan persisten. CRUD juga terkadang digunakan untuk mendeskripsikan konvensi interface pengguna yang memfasilitasi tampilan, pencarian, dan perubahan informasi menggunakan formulir dan laporan berbasis komputer.
CRUD adalah empat fungsi dasar yang seharusnya dapat dilakukan oleh sebuah model.
Dapat juga diartikan sebagai fungsi menulis (Write)
. Dengan kata lain, penambahan data ke dalam database ditampung dalam fungsi ini, sehingga fungsi ini biasa menggunakan method POST
.
Biasanya digunakan untuk menampilkan form yang diisi untuk memasukkan data.
public function create()
{
$angkatans = Angkatan::all();
return view('camin.create', compact('angkatans'));
}
@extends('master')
@section('content')
<h1>Tambah Camin</h1>
<form action="/camin" method="POST" enctype="multipart/form-data">
@csrf
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-body">
<div class="form-group">
<label>Name</label>
<input type="text" class="form-control" name="nama" placeholder="Enter Name">
@error('nama')
<div class="alert alert-danger">
{{ $message }}
</div>
@enderror
</div>
<div class="form-group">
<label>NRP</label>
<input type="text" class="form-control" name="nrp" placeholder="Enter NRP">
@error('nrp')
<div class="alert alert-danger">
{{ $message }}
</div>
@enderror
</div>
<div class="form-group">
<label>Jurusan</label>
<input type="text" class="form-control" name="jurusan" placeholder="Enter Jurusan">
@error('jurusan')
<div class="alert alert-danger">
{{ $message }}
</div>
@enderror
</div>
<div class="form-group">
<label>Angkatan</label>
<select class="form-select form-control" name="angkatan_id">
<option selected value="">-</option>
@foreach ($angkatans as $item)
<option value="{{$item->id}}">{{$item->nama}}</option>
@endforeach
</select>
</div>
<div class="d-flex justify-content-between">
<a href="./" class="btn btn-light"><< Back</a>
<button type="submit" class="btn btn-primary" style="border-radius: 3px">
<i class="nav-icon fas fa-plus-circle"></i>
Add Camin
</button>
</div>
</div>
</div>
</div>
</div>
</form>
@endsection
Masuk ke router pada /routes/web.php
, tulis:
Route::get('/camin', [CaminController::class, 'create']);
Note: Pastikan web.php
sudah memuat use App\Http\Controllers\CaminController;
Digunakan untuk menyimpan data yang telah diinput ke database
public function store(Request $request)
{
$request->validate([
'nama' => 'required',
'nrp' => 'required|numeric',
'jurusan' => 'required',
'angkatan_id' => 'required',
],
[
'nama.required' => 'Name can\'t be empty!',
'nrp.required' => 'NRP can\'t be empty!',
'jurusan.required' => 'Jurusan can\'t be empty!',
'angkatan_id' => 'Please choose your angkatan',
]);
Camin::create([
'nama' => $request->nama,
'angkatan_id' => $request->angkatan_id,
'nrp' => $request->nrp,
'jurusan' => $request->jurusan,
]);
return redirect('/camin');
}
Route::post('/camin', [CaminController::class, 'store']);
Pada fungsi Read
, kita dapat menampilkan data yang telah disimpan dalam database. Maka dari itu, fungsi ini biasa menggunakan method GET
. Read
sendiri dapat kita bedakan menjadi index
atau show
pada fungsi bawaan controller
di Laravel.
Biasanya digunakan untuk menampilkan seluruh entry data, namun yang ditampilkan hanya data yang necessary untuk membedakan data satu dengan yang lain, seperti id
, name
, dan dsb.
Tulis kode di bawah pada fungsi index()
di CaminController
public function index()
{
$camins = Camin::all();
return view('camin.index', compact('camins'));
}
Buatlah sebuah view yang dapat menampilkan data $camins
yang sudah kita dapatkan. Apabila kita mengikuti return view('camin.index', compact('camins'))
, maka, masuk ke folder /public/resources/view
:
a. Buat sebuah master layout (master.blade.php
)
<!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>Camin Alpro 2023</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" integrity="sha512-9usAa10IRO0HhonpyAIVpjrylPvoDwiPUiKdWk5t3PyolY1cOd4DSE0Ga+ri4AuTroPR5aQvXU9xC6qOPnzFeg==" crossorigin="anonymous" referrerpolicy="no-referrer" />
</head>
<body>
@include('navbar')
<div class="content mx-5 my-4">
@yield('content')
</div>
</body>
</html>
b. Buat navbar (navbar.blade.php
)
<div class="bg-dark text-white py-2">
<div class="d-flex justify-content-between mx-5">
<h3>Camin Alpro 2023</h3>
<div class="d-flex justify-content-around text-primary align-items-center">
<h5 class="mx-2">
<a href="/">Home</a>
</h5>
<h5 class="mx-2">
<a href="/camin">Camin</a>
</h5>
<h5 class="mx-2">
<a href="/#">About Us</a>
</h5>
</div>
</div>
</div>
c. Buat folder bernama camin
, buatlah index.blade.php
di dalamnya:
@extends('master')
@section('content')
<h1>Daftar Camin 2023</h1>
<div class="row mx-5 justify-content-center">
@foreach ($camins as $item)
<div class="card mx-3" style="width: 20rem;">
<div class="card-body d-flex flex-column align-items-center">
<h4 class="my-3">Camin {{$item->id}}</h4>
<i class='fas fa-id-badge' style='font-size:180px'></i>
<div class="data my-3">
<h5>Nama: {{$item->nama}}</h5>
<h5>Angkatan: {{$item->angkatan->nama}}</h5>
</div>
<a href="/camin/{{$item->id}}" class="btn btn-sm btn-info">Details</a>
</div>
</div>
@endforeach
</div>
<div class="d-flex mx-5 my-5 justify-content-center">
<a href="/camin/create" class="btn btn-primary mx-5" style="max-width: 18rem;">+Add Camin</a>
</div>
@endsection
Masuk ke router pada /routes/web.php
, tulis:
Route::get('/camin', [CaminController::class, 'index']);
Maka, apabila kita membuka http://localhost:8000/camin , muncul tampilan:
Biasanya digunakan untuk sebuah data dengan id
tertentu secara detail.
Tulis kode di bawah pada fungsi show()
di CaminController
public function show($id)
{
$camin = Camin::findorfail($id);
return view('camin.show', compact('camin'));
}
Buatlah sebuah view yang dapat menampilkan data $camin
yang sudah kita dapatkan pada show.blade.php
:
@extends('master')
@section('content')
<h1>Detail Camin {{$camins->id}}</h1>
<div class="d-flex justify-content-center my-5">
<div class="card col-sm-6 mx-3 w-50 p-5 bg-dark text-white">
<div class="card-body d-flex justify-content-center">
<i class='fas fa-id-badge' style='font-size:180px' class="mx-5"></i>
<div class="data m-4 align-items-center">
<h5>Nama: {{$camins->nama}}</h5>
<h5>NRP: {{$camins->nrp}}</h5>
<h5>Jurusan: {{$camins->jurusan}}</h5>
<h5>Angkatan: {{$camins->angkatan->nama}}</h5>
</div>
</div>
<div class="d-flex justify-content-between">
<a href="/camin" class="btn btn-light mt-4 mx-2"><< Back</a>
<div class="d-flex justify-content-end">
<a href="/camin/{{$camins->id}}/edit" class="btn btn-warning mt-4 mx-2">Edit</a>
</div>
</div>
</div>
</div>
@endsection
Masuk ke router pada /routes/web.php
, tulis:
Route::get('/camin/{camins_id}', [CaminController::class, 'show']);
Maka, apabila kita membuka http://localhost:8000/camin/1 , muncul tampilan:
Pada fungsi Update
, kita dapat memperbaru data yang telah disimpan dalam database. Sehingga kita akan menggunakan fungsi edit()
dan update()
yang mirip dengan fungsi create()
dan store()
sebelumnya. Namun, untuk fungsi update()
menggunakan method PUT
Berfungsi sama dengan fungsi create()
, namun biasanya value yang telah ada ditampilkan.
public function edit($id)
{
$camins = Camin::findorfail($id);
$angkatans = Angkatan::all();
return view('camin.edit', compact('camins', 'angkatans'));
}
Buat view dengan nama edit.blade.php
@extends('master')
@section('content')
<h1>Edit Camin {{$camins->id}}</h1>
<form action="/camin/{{$camins->id}}" method="POST" enctype="multipart/form-data">
@csrf
@method('put')
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-body">
<div class="form-group">
<label>Name</label>
<input type="text" class="form-control" name="nama" value="{{$camins->nama}}" placeholder="Enter Name">
@error('nama')
<div class="alert alert-danger">
{{ $message }}
</div>
@enderror
</div>
<div class="form-group">
<label>NRP</label>
<input type="text" class="form-control" name="nrp" value="{{$camins->nrp}}" placeholder="Enter NRP">
@error('nrp')
<div class="alert alert-danger">
{{ $message }}
</div>
@enderror
</div>
<div class="form-group">
<label>Jurusan</label>
<input type="text" class="form-control" name="jurusan" value="{{$camins->jurusan}}" placeholder="Enter Jurusan">
@error('jurusan')
<div class="alert alert-danger">
{{ $message }}
</div>
@enderror
</div>
<div class="form-group">
<label>Angkatan</label>
<select class="form-select form-control" name="angkatan_id">
<option value="">-</option>
@foreach ($angkatans as $temp)
<option value="{{$temp-> id}}"
@if($temp->id == $camins->angkatan->id)
selected
@endif
>{{$temp->nama}}</option>
@endforeach
</select>
</div>
<div class="d-flex justify-content-between">
<a href="./" class="btn btn-light"><< Back</a>
<button type="submit" class="btn btn-primary" style="border-radius: 3px">
<i class="nav-icon fas fa-save"></i>
Update
</button>
</div>
</div>
</div>
</div>
</div>
</form>
@endsection
Masuk ke router pada /routes/web.php
, tulis:
Route::get('/camin/{camins_id}/edit', [CaminController::class, 'edit']);
Digunakan untuk menyimpan data yang telah diinput ke database
public function update(Request $request, $id)
{
$request->validate([
'nama' => 'required',
'nrp' => 'required|numeric',
'jurusan' => 'required',
'angkatan_id' => 'required',
],
[
'nama.required' => 'Name can\'t be empty!',
'nrp.required' => 'NRP can\'t be empty!',
'jurusan.required' => 'Jurusan can\'t be empty!',
'angkatan_id' => 'Please choose your angkatan',
]);
$camin = Camin::findorfail($id);
$camin_data = [
'nama' => $request->nama,
'angkatan_id' => $request->angkatan_id,
'nrp' => $request->nrp,
'jurusan' => $request->jurusan,
];
$camin->update($camin_data);
return view('camin.show', compact('camin'));
}
Route::put('/camin/{camins_id}', [CaminController::class, 'update']);
Pada fungsi Delete
, kita dapat menghapus data yang telah disimpan dalam database. Fungsi ini menggunakan method DELETE
public function destroy($id)
{
$camins = Camin::findorfail($id);
$camins->delete();
return redirect('/camin');
}
Pada show.blade.php
, tulis:
@extends('master')
@section('content')
<h1>Detail Camin {{$camins->id}}</h1>
<div class="d-flex justify-content-center my-5">
<div class="card col-sm-6 mx-3 w-50 p-5 bg-dark text-white">
<div class="card-body d-flex justify-content-center">
<i class='fas fa-id-badge' style='font-size:180px' class="mx-5"></i>
<div class="data m-4 align-items-center">
<h5>Nama: {{$camins->nama}}</h5>
<h5>NRP: {{$camins->nrp}}</h5>
<h5>Jurusan: {{$camins->jurusan}}</h5>
<h5>Angkatan: {{$camins->angkatan->nama}}</h5>
</div>
</div>
<form action="/camin/{{$camins->id}}" method="POST" enctype="multipart/form-data">
@csrf
@method('delete')
<div class="d-flex justify-content-between">
<a href="/camin" class="btn btn-light mt-4 mx-2"><< Back</a>
<div class="d-flex justify-content-end">
<a href="/camin/{{$camins->id}}/edit" class="btn btn-warning mt-4 mx-2">Edit</a>
<input type="submit" class="btn btn-danger mt-4 mx-2" value="Delete">
</div>
</div>
</form>
</div>
</div>
@endsection
Route::delete('/camin/{camins_id}', [CaminController::class, 'destroy']);
Pada Laravel 9, kini kita bisa menggunakan grouping agar list route kita dapat disusun dengan clean dan menjadi lebih readable
Kembalilah pada web.php
, maka tampilan kurang lebih akan seperti ini:
Kalian dapat mengubahnya menjadi seperti potongan kode di bawah ini:
Route::controller(CaminController::class)->group(function () {
Route::get('/camin', 'index');
Route::get('/camin/create', 'create');
Route::post('/camin', 'store');
Route::get('/camin/{camins_id}', 'show');
Route::get('/camin/{camins_id}/edit', 'edit');
Route::put('/camin/{camins_id}', 'update');
Route::delete('/camin/{camins_id}', 'destroy');
});
Untuk repository, dapat diakses di: https://github.com/Kadigas/camin-2023