0% menganggap dokumen ini bermanfaat (0 suara)
15 tayangan122 halaman

Modul Wp3 Update

Modul Web Programming III ini dirancang untuk mahasiswa dalam memahami pembuatan web menggunakan Framework Laravel, dengan fokus pada pembuatan halaman Administrator (Back-End). Materi mencakup instalasi, manajemen basis data, dan implementasi tampilan frontend yang responsif. Di akhir perkuliahan, mahasiswa diharapkan dapat menyelesaikan proyek akhir yang harus dipresentasikan sebagai syarat kelulusan.

Diunggah oleh

awakmuuu01
Hak Cipta
© © All Rights Reserved
Kami menangani hak cipta konten dengan serius. Jika Anda merasa konten ini milik Anda, ajukan klaim di sini.
Format Tersedia
Unduh sebagai PDF, TXT atau baca online di Scribd
0% menganggap dokumen ini bermanfaat (0 suara)
15 tayangan122 halaman

Modul Wp3 Update

Modul Web Programming III ini dirancang untuk mahasiswa dalam memahami pembuatan web menggunakan Framework Laravel, dengan fokus pada pembuatan halaman Administrator (Back-End). Materi mencakup instalasi, manajemen basis data, dan implementasi tampilan frontend yang responsif. Di akhir perkuliahan, mahasiswa diharapkan dapat menyelesaikan proyek akhir yang harus dipresentasikan sebagai syarat kelulusan.

Diunggah oleh

awakmuuu01
Hak Cipta
© © All Rights Reserved
Kami menangani hak cipta konten dengan serius. Jika Anda merasa konten ini milik Anda, ajukan klaim di sini.
Format Tersedia
Unduh sebagai PDF, TXT atau baca online di Scribd
Anda di halaman 1/ 122

COVER

MODUL
PROGRAMMING III

FAKULTAS TEKNIK DAN INFORMATIKA


UNIVERSITAS BINA SARANA INFORMATI
KATA PENGANTAR

Segala puji dan syukur kami panjatkan ke hadirat Tuhan Yang Maha Esa, karena berkat rahmat-
Nya penulisan modul Mata Kuliah Web Programming III ini dapat terselesaikan dengan baik.
Modul ini disusun untuk memenuhi kebutuhan mahasiswa dalam mata kuliah Web
Programming III, yang disajikan dalam bentuk praktikum dan diharapkan dapat membekali
mahasiswa dalam memahami pembuatan web menggunakan Framework Laravel.

Modul Web Programming III ini membahas materi yang dibatasi hingga pembuatan halaman
Administrator (Back-End). Di akhir perkuliahan, mahasiswa diharapkan mampu
mengimplementasikan materi yang telah dipelajari dalam bentuk final project yang harus
dipresentasikan sebagai syarat kelulusan mata kuliah Web Programming II. Teknik penyajian
dalam modul ini dilakukan secara terpadu dan sistematis.

Sebagai sebuah modul pembelajaran, pembahasan diawali dengan menjelaskan target


pembelajaran yang hendak dicapai. Dengan demikian, pengguna modul ini dapat secara
mandiri mengukur tingkat pemahaman dan ketuntasan yang telah dicapai.

Kami menyadari bahwa modul ini masih memiliki banyak kekurangan. Oleh karena itu, kami
dengan lapang dada menerima segala masukan dan kritik yang konstruktif dari berbagai pihak
demi kesempurnaan modul ini di masa yang akan datang. Semoga modul ini dapat bermanfaat
bagi para pembaca.

Jakarta, Januari 2025

Tim Penulis
DAFTAR ISI

COVER.................................................................................................................................................. i
KATA PENGANTAR .......................................................................................................................... ii
DAFTAR ISI ........................................................................................................................................iii
Minggu Ke-1 Instalasi & Konfigurasi Pada Laravel ............................................................................ 1
Minggu Ke-2 Mengenal Controller, Function, Route & View .............................................................. 5
Minggu Ke-3 Manajemen Basis Data: Konfigurasi Databases, Migration, dan Seeders ..................... 32
Minggu Ke-4 CRUD (Create, Read, Update, Delete) ......................................................................... 48
Minggu Ke-5 Authenticate (Logika Login) & Pengujian Unit ............................................................ 52
Minggu Ke-6 Template HTML, CSS, Bootstrap, dan JavaScript ....................................................... 59
Minggu Ke-7 Implementasi DataTable dan Form dengan Template................................................... 66
Minggu Ke-9 Manajemen Data Master ............................................................................................... 73
Minggu Ke-10 Data Join Tabel Part 1 .................................................... Error! Bookmark not defined.
Minggu Ke-11 Data Join Tabel Part 2 .................................................... Error! Bookmark not defined.
Minggu Ke-12 Laporan Data Master ...................................................... Error! Bookmark not defined.
Minggu Ke-13 Persentasi Tugas Kelompok...................................................................................... 117
Minggu Ke-14 Persentasi Tugas Kelompok...................................................................................... 118
Minggu Ke-15 Persentasi Tugas Kelompok...................................................................................... 119
Minggu Ke-1
Pengenalan Web Frontend Development

Sebelum memulai materi pada Web Programming III, silakan lakukan pembaruan Portofolio,
yaitu dengan menambahkan unit unjuk kerja kompetensi yang telah dikerjakan pada Web
Programming II di setiap pertemuannya. Dengan demikian, Portofolio yang Anda miliki akan
menjadi lebih lengkap, detail, dan sempurna sesuai dengan standar Impentasi Unit Kompetensi
Software Development.
✓ Web Programming II berfocus pad halaman backend
✓ Web Programming III berfocus pada halaman frontend
Berikut kami tampilkan kembali salah satu skema dari Lembaga Sertifikasi Profesi (LSP)
Universitas Bina Sarana Informatika, yaitu skema Programmer pada Tabel 1.1 dan skema
Analis Program pada Tabel 1.2

Tabel 1.1
Skema Programmer

No Kode Unit Judul Unit Jenis Standar (Standar


Khusus/Standar
Internasional/SKKNI)
1. J. 620100.017.02 Mengimplementasikan Pemrograman SKKNI
Terstruktur

2. J. 620100.016.01 Menulis Kode Dengan Prinsip Sesuai SKKNI


Guidelines dan Best Practice

3. J. 620100.025.02 Melakukan Debugging SKKNI

4. J. 620100.023.02 Membuat Dokumen Kode Program SKKNI

5. J.620100.009.01 Menggunakan Spesifikasi Program SKKNI

6. J. 620100.018.02 Mengimplementasikan pemrograman SKKNI


berorientasi objek

7. J. 620100.019.02 Menggunakan Library atau Komponen Pre- SKKNI


Existing

8. J. 620100.021.02 Menerapkan Akses Basis Data SKKNI

9. J. 620100.033.02 Melaksanakan Pengujian Unit Program SKKNI

WEB PROGRAMMING III 1


Tabel 1.2
Skema Analis Program

No Kode Unit Judul Unit Jenis Standar (Standar


Khusus/Standar
Internasional/SKKNI)

1. J.620100.002.01 Menganalisis skalabilitas perangkat lunak SKKNI

2. J.620100.020.02 Menggunakan sql SKKNI

3. J.620100.021.02 Menerapkan akses basis data SKKNI

4. J.620100.022.02 Mengimplementasikan algoritma SKKNI


pemrograman

5. J.620100.023.02 Membuat dokumen kode program SKKNI

6. J.620100.025.02 Melakukan debugging SKKNI

7. J.620100.032.01 Menerapkan code review SKKNI

8. J.620100.033.02 Melaksanakan pengujian unit program SKKNI

9. J.620100.034.02 Melaksanakan pengujian integrasi program SKKNI

WEB PROGRAMMING III 2


Apa itu Frontend Development?
Frontend development adalah proses pembangunan bagian dari sebuah website atau aplikasi
web yang langsung berinteraksi dengan pengguna (user interface). Fokus utama frontend
development adalah memastikan tampilan website menarik, responsif, dan mudah digunakan.

Prinsip Dasar Frontend Development


1. Responsive Design: Memastikan website dapat diakses di berbagai perangkat (desktop,
tablet, mobile).
2. User Experience (UX): Membuat website mudah digunakan dan nyaman bagi pengguna.
3. Accessibility: Memastikan website dapat diakses oleh semua orang, termasuk
penyandang disabilitas.
4. Menghubungkan tampilan dengan backend

Sebelumnya, pada Modul Web Programming II, kita telah menggunakan template Matrix
Admin. Pada Modul Web Programming III kali ini, kita akan beralih menggunakan template
E-Shop. E-Shop adalah template website e-commerce gratis yang responsif, sehingga dapat
diakses dengan baik di berbagai perangkat. Template ini dapat diunduh melalui tautan berikut:
https://bit.ly/LaravelWebPro3

1. Download template pada folder Template\eshop.zip, kemudian simpan pada direktori


TokoOnline\public\frontend dan ekstrak file tersebut (hasil ekstraksi untuk demo template).
Untuk menjalankan demo, dapat langsung menggunakan browser atau klik kanan dan pilih
browser yang diinginkan

2. pada direktori C:\Laravel\tokoonline\public\frontend\eshop copy folder css, fonts, img, js


ke C:\Laravel\tokoonline\public\frontend

WEB PROGRAMMING III 3


3. Pada folder Template\app.blade.php silahkan di download dan simpan pada projek dengan
direktori resources/views /v_layouts. Dimana app.blade.php ini adalah hasil implementasi
dari template index.html dan products.html.

4. Kemudian pada controller yakni BerandaController.php kita tambahkan public function


index(), Berikut script lengkapnya:
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Produk;

class BerandaController extends Controller


{
public function berandaBackend()
{
return view('backend.v_beranda.index', [
'judul' => 'Beranda',
'sub' => 'Halaman Beranda'

WEB PROGRAMMING III 4


]);
}

public function index()


{
$produk = Produk::where('status', 1)->orderBy('updated_at', 'desc')->paginate(6);
return view('v_beranda.index', [
'judul' => 'Halan Beranda',
'produk' => $produk,
]);
}
}

Dimana kita diminta untuk membuat folder v_beranda dengan file index.blade.php dengan
script sebagai berikut:
@extends('v_layouts.app')
@section('content')
<!-- template -->
<!-- end template-->
@endsection

5. Tambahkan script Pada routes\web.php


Route::get('/', function () {
// return view('welcome');
return redirect()->route('beranda');
});

// Frontend
Route::get('/beranda', [BerandaController::class, 'index'])->name('beranda');

Dan berikut script lengkap dari routes\web.php


<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\BerandaController;
use App\Http\Controllers\LoginController;
use App\Http\Controllers\UserController;
use App\Http\Controllers\KategoriController;
use App\Http\Controllers\ProdukController;

Route::get('/', function () {
// return view('welcome');
return redirect()->route('beranda');

WEB PROGRAMMING III 5


});

Route::get('backend/beranda', [BerandaController::class, 'berandaBackend'])-


>name('backend.beranda')->middleware('auth');

Route::get('backend/login', [LoginController::class, 'loginBackend'])-


>name('backend.login');
Route::post('backend/login', [LoginController::class, 'authenticateBackend'])-
>name('backend.login');
Route::post('backend/logout', [LoginController::class, 'logoutBackend'])-
>name('backend.logout');

// Route untuk User


// Route::resource('backend/user', UserController::class)->middleware('auth');
Route::resource('backend/user', UserController::class, ['as' => 'backend'])-
>middleware('auth');
// Route untuk laporan user
Route::get('backend/laporan/formuser', [UserController::class, 'formUser'])-
>name('backend.laporan.formuser')->middleware('auth');
Route::post('backend/laporan/cetakuser', [UserController::class, 'cetakUser'])-
>name('backend.laporan.cetakuser')->middleware('auth');

// Route untuk Kategori


Route::resource('backend/kategori', KategoriController::class, ['as' => 'backend'])-
>middleware('auth');

// Route untuk Produk


Route::resource('backend/produk', ProdukController::class, ['as' => 'backend'])-
>middleware('auth');
// Route untuk menambahkan foto
Route::post('foto-produk/store', [ProdukController::class, 'storeFoto'])-
>name('backend.foto_produk.store')->middleware('auth');
// Route untuk menghapus foto
Route::delete('foto-produk/{id}', [ProdukController::class, 'destroyFoto'])-
>name('backend.foto_produk.destroy')->middleware('auth');
// Route untuk laporan produk
Route::get('backend/laporan/formproduk', [ProdukController::class, 'formProduk'])-
>name('backend.laporan.formproduk')->middleware('auth');
Route::post('backend/laporan/cetakproduk', [ProdukController::class, 'cetakProduk'])-
>name('backend.laporan.cetakproduk')->middleware('auth');

// Frontend
Route::get('/beranda', [BerandaController::class, 'index'])->name('beranda');

WEB PROGRAMMING III 6


6. Sehingga saat aplikasi dijalankan http://localhost:8000/

Banner

Footer

WEB PROGRAMMING III 7


Silakan sesuaikan konten dengan tema yang diambil, terutama pada bagian header dan footer.
Pastikan informasi yang ditampilkan relevan dengan tujuan dan identitas website. Jika logo
Universitas BSI tidak tampil, pastikan folder image dengan file logo.png tersedia di direktori
public. Jika file tidak ditemukan atau ukurannya tidak sesuai dengan gambar di atas, silakan
unduh melalui tautan berikut: https://bit.ly/LaravelWebPro3 pada folder Template\image.rar.
Kemudian ekstrak file image.rar di dalam folder public. (Langkah ini juga telah dilakukan pada
modul sebelumnya.)

6. Untuk mengubah tampilan banner yakni kita buka views\v_layouts\app.blade.php kita


lakukkan pencarian pada komentar yakni <!-- banner -->

Kita ubah direktori gambar ke Folder banner yang sesuai dengan tema, misalnya sebagai
contoh yakni penjualan jajanan tradisional sebagi toko makanan khas Indonesia. Bisa di
dowonload https://bit.ly/LaravelWebPro3 dengan folder Template\banner.rar & ekstrak
dengan direktori public\frontend\banner sebagai berikut:

Sehingga perubahan script pada app.blade.php sebagai berikut:


<div id="home">
<!-- container -->
<div class="container">
<!-- home wrap -->
<div class="home-wrap">
<!-- home slick -->
<div id="home-slick">

WEB PROGRAMMING III 8


<!-- banner -->
<div class="banner banner-1">
<img src="{{ asset('frontend/banner/banner01.jpg') }}" alt="">
<div class="banner-caption text-center">
<h1>Jajanan Tradisional</h1>
<h3 class="font-weak" style="color: 30323a;">Khas Makanan
Indonesia</h3>
<button class="primary-btn">Pesan Sekarang</button>
</div>
</div>
<!-- /banner -->

<!-- banner -->


<div class="banner banner-1">
<img src="{{ asset('frontend/banner/banner02.jpg') }}" alt="">
<div class="banner-caption">
<h1 class="primary-color">Khas Makanan Indonesia<br><span
class="white-color font-weak">Jajanan Tradisional</span></h1>
<button class="primary-btn">Pesan Sekarang</button>
</div>
</div>
<!-- /banner -->

<!-- banner -->


<div class="banner banner-1">
<img src="{{ asset('frontend/banner/banner03.jpg') }}" alt="">
<div class="banner-caption">
<h1 style="color: f8694a;">Khas Makanan <span>Indonesia</span></h1>
<button class="primary-btn">Pesan Sekarang</button>
</div>
</div>
<!-- /banner -->
</div>
<!-- /home slick -->
</div>
<!-- /home wrap -->
</div>
<!-- /container -->
</div>

Catatan:
Gunakan ukuran gambar banner yang sesuai dengan ukuran asli gambar banner yang
disediakan dalam template.

WEB PROGRAMMING III 9


Minggu Ke-2
Desain dan Implementasi Tampilan Produk

Tampilan produk merupakan elemen utama dalam e-commerce yang berperan penting dalam
memberikan pengalaman pengguna (UX) yang baik. Pengguna harus dapat dengan mudah
melihat daftar produk, mendapatkan informasi yang jelas, serta melakukan pencarian dan filter
sesuai kebutuhan mereka. Oleh karena itu, desain antarmuka harus responsif, menarik, dan
intuitif agar meningkatkan kenyamanan serta kemudahan dalam berbelanja.

Pada materi ini, kita akan mempelajari cara menampilkan daftar produk dengan tata letak yang
menarik, serta memastikan tampilan responsif agar dapat diakses dengan baik di berbagai
perangkat. Implementasi ini akan menjadi dasar dalam membangun pengalaman pengguna
yang lebih interaktif dan fungsional pada website e-commerce.

1. Tambahkan data produk melalui backend minimal 8 record misalnya 7 record dengan status
Publis dan 1 status Blok

WEB PROGRAMMING III 10


2. Di direktori resources/views v_layouts/app.blade.php, cari komentar <!-- Product Single -
-> Setelah itu, blok bagian dari <div class="row"> hingga </div>, kemudian klik kanan dan
pilih opsi Cut seperti terlihat pada gambar dibawah ini:

3. Kemudian paste pada direktori resources/ views/v_beranda/index.blade.php sehingga


akan terlihat sebagai berikut:

4. Kembali ke direktori resources/views/v_layouts/app.blade.php, tambahkan @yield untuk


menggantikan posisi baris kode komentar <!-- Product Single -->, sehingga tampilannya akan
seperti gambar berikut:
<!-- @yieldAwal -->
@yield('content')
<!-- @yieldAkhir-->

WEB PROGRAMMING III 11


5. Selanjutnya, lakukan perulangan pada resources/views/v_beranda/index.blade.php
berdasarkan fungsi index() yang ada pada BerandaController.php, sehingga berikut
perubahan script pada index.blade.php
@extends('v_layouts.app')
@section('content')
<!-- template -->

<!-- STORE -->


<div id="store">
<!-- row -->
<div class="row">
@foreach ($produk as $row)
<!-- Product Single -->
<div class="col-md-4 col-sm-6 col-xs-6">
<div class="product product-single">
<div class="product-thumb">
<div class="product-label">
<span>Kategori</span>
<span class="sale">{{ $row->kategori->nama_kategori }}</span>
</div>

<a href="#">
<button class="main-btn quick-view"><i class="fa fa-search-
plus"></i> Detail Produk</button>
</a>
<img src="{{ asset('storage/img-produk/thumb_md_' . $row->foto) }}"
alt="">
</div>
<div class="product-body">
<h3 class="product-price"> Rp. {{ number_format($row->harga, 0, ',',
'.') }} <span
class="product-old-price">{{ $row->kategori->nama_kategori
}}</span></h3>

<h2 class="product-name"><a href="#">{{ $row->nama_produk }}</a></h2>


<div class="product-btns">
<a href="#" title="Detail Produk">
<button class="main-btn icon-btn"><i class="fa fa-search-
plus"></i></button>
</a>
<form action="3" method="post"
style="display: inline-block;" title="Pesan Ke Aplikasi">
@csrf
<button type="submit" class="primary-btn add-to-cart"><i
class="fa fa-shopping-cart"></i> Pesan</button>

WEB PROGRAMMING III 12


</form>
</div>
</div>
</div>
</div>
<!-- /Product Single -->
@endforeach
<div class="clearfix visible-md visible-lg visible-sm visible-xs"></div>
</div>
<!-- /row -->
</div>
<!-- /STORE -->

<!-- end template-->


@endsection

6. Berikut hasil perulangan fungsi index() yang ada pada BerandaController.php, dimana
produk yang ditampilkan hanya 6 produk terbaru saja.

WEB PROGRAMMING III 13


7. Sedangkan untuk menampilkan detail produk, ini bisa kita tambahkan script pada
ProdukController
public function detail($id)
{
$fotoProdukTambahan = FotoProduk::where('produk_id', $id)->get();
$detail = Produk::findOrFail($id);
$kategori = Kategori::orderBy('nama_kategori', 'desc')->get();
return view('v_produk.detail', [
'judul'=> 'Detail Produk',
'kategori' => $kategori,
'row' => $detail,
'fotoProdukTambahan' => $fotoProdukTambahan
]);
}

7. Dengan demikian kita tambahkan file detail.blade.php pada direktori pada


resources/views/v_produk. Detail.blade.php merupakan hasil dari implementasi product-
page.html. Berikut full script dari detail.blade.php:

@extends('v_layouts.app')
@section('content')
<!-- template -->

<!-- row -->


<div class="row">
<div class="col-md-12">
<div class="billing-details">
<div class="section-title">
<h3 class="title">{{ $judul }} </h3>
</div>
</div>
</div>
<!-- Product Details -->
<div class="product product-details clearfix">
<div class="col-md-6">
<div id="product-main-view">
<div class="product-view">
<img src="{{ asset('storage/img-produk/thumb_lg_' . $row->foto) }}"
alt="">
</div>
@foreach ($fotoProdukTambahan as $item)
<div class="product-view">
@if ($item->produk_id == $row->id)
<img src="{{ asset('storage/img-produk/' . $item->foto) }}" alt="">
@else
@endif
</div>
@endforeach
</div>
<div id="product-view">

WEB PROGRAMMING III 14


<div class="product-view">
<img src="{{ asset('storage/img-produk/thumb_sm_' . $row->foto) }}"
alt="">
</div>
@foreach ($fotoProdukTambahan as $item)
<div class="product-view">
@if ($item->produk_id == $row->id)
<img src="{{ asset('storage/img-produk/' . $item->foto) }}" alt="">
@else
@endif
</div>
@endforeach
</div>
</div>
<div class="col-md-6">
<div class="product-body">
<div class="product-label">
<span>Kategori</span>
<span class="sale">{{ $row->kategori->nama_kategori }}</span>
</div>
<h2 class="product-name">{{ $row->nama_produk }}</h2>
<h3 class="product-price">Rp. {{ number_format($row->harga, 0, ',', '.') }}
</h3>
<p>
{!! $row->detail !!}
</p>
<div class="product-options">
<ul class="size-option">
<li><span class="text-uppercase">Berat:</span></li>
{{ $row->berat }} Gram
</ul>
<ul class="size-option">
<li><span class="text-uppercase">Stok:</span></li>
{{ $row->stok }}
</ul>
</div>

<div class="product-btns">
<form action="#" method="post"
style="display: inline-block;">
@csrf
<button type="submit" class="primary-btn add-to-cart"><i class="fa
fa-shopping-cart"></i>
Pesan</button>
</form>
</div>
</div>
</div>

</div>
</div>
<!-- /Product Details -->

<!-- end template-->


@endsection

8. Tambahkan script Pada routes\web.php untuk menterjemahkan function detail pada


ProdukController
Route::get('/produk/detail/{id}', [ProdukController::class, 'detail'])-
>name('produk.detail');

WEB PROGRAMMING III 15


Dengan demikian, kita dapat menambahkan tautan di setiap aksi yang menuju halaman detail
produk dengan menambahkan {{ route('produk.detail', $row->id) }}. Misalnya kita tambahkan
pada resources/views/v_beranda/index.blade.php

9. Saat diklik salah satu produk dengan aksi Detail Produk maka akan tampilan ke halaman
detail produk. Namun, banner tetap tampil. Kita dapat menyembunyikannya banner, dimana
banner hanya akan muncul pada halaman beranda saja.

b. Kategori a. Banner

WEB PROGRAMMING III 16


10. Buka file resources/views/v_layouts/app.blade.php¸ kita ubah script dengan melakukan
pencarian dengan komentar id="home"
@if (request()->segment(1) == '' || request()->segment(1) == 'beranda')

@endif

a. Banner

b. Kategori, cari komentar <!-- category nav --> kemudian berikut perubahan pada blok kode
<!-- category nav --> sampai <!-- /category nav -->

@php
$kategori = DB::table('kategori')->orderBy('nama_kategori', 'asc')->get();
@endphp
@if (request()->segment(1) == '' || request()->segment(1) == 'beranda')
<!-- category nav -->
<div class="category-nav">
<span class="category-header">Kategori <i class="fa fa-list"></i></span>

WEB PROGRAMMING III 17


<ul class="category-list">
@foreach ($kategori as $row)
<li><a href="{{ route('produk.kategori', $row->id) }}">{{ $row->nama_kategori
}}</a></li>
@endforeach
</ul>

<ul class="category-list">
</div>
@else
<div class="category-nav show-on-click">
<span class="category-header">Kategori <i class="fa fa-list"></i></span>
<ul class="category-list">
@foreach ($kategori as $row)
<li><a href="{{ route('produk.kategori', $row->id) }}">{{ $row->nama_kategori
}}</a></li>
@endforeach
</ul>
</div>
<!-- /category nav -->
@endif

Tambahkan script Pada routes\web.php untuk menterjemahkan 'produk.kategori'


Route::get('/produk/kategori/{id}', [ProdukController::class, 'produkKategori'])-
>name('produk.kategori');

Demikian juga kita tambahkan function produkKategori pada ProdukController:


public function produkKategori($id)
{
$kategori = Kategori::orderBy('nama_kategori', 'desc')->get();
$produk = Produk::where('kategori_id', $id)->where('status', 1)-
>orderBy('updated_at', 'desc')->paginate(6);
return view('v_produk.produkkategori', [
'judul' => 'Filter Kategori',
'kategori' => $kategori,
'produk' => $produk,
]);
}

Dengan demikian kita tambahkan file produkkategori.blade.php dalam folder v_produk


@extends('v_layouts.app')
@section('content')
<!-- template -->

<!-- STORE -->


<div id="store">
<!-- row -->
<div class="row">
@foreach ($produk as $row)
<!-- Product Single -->
<div class="col-md-4 col-sm-6 col-xs-6">
<div class="product product-single">
<div class="product-thumb">
<div class="product-label">
<span>Kategori</span>
<span class="sale">{{ $row->kategori->nama_kategori }}</span>
</div>

<a href="{{ route('produk.detail', $row->id) }}">


<button class="main-btn quick-view"><i class="fa fa-search-
plus"></i> Detail Produk</button>
</a>
<img src="{{ asset('storage/img-produk/thumb_md_' . $row->foto) }}"
alt="">

WEB PROGRAMMING III 18


</div>
<div class="product-body">
<h3 class="product-price"> Rp. {{ number_format($row->harga, 0, ',',
'.') }} <span
class="product-old-price">{{ $row->kategori->nama_kategori
}}</span></h3>

<h2 class="product-name"><a href="#">{{ $row->nama_produk }}</a></h2>


<div class="product-btns">
<a href="{{ route('produk.detail', $row->id) }}" title="Detail
Produk">
<button class="main-btn icon-btn"><i class="fa fa-search-
plus"></i></button>
</a>
<form action="3" method="post"
style="display: inline-block;" title="Pesan Ke Aplikasi">
@csrf
<button type="submit" class="primary-btn add-to-cart"><i
class="fa fa-shopping-cart"></i> Pesan</button>
</form>
</div>
</div>
</div>
</div>
<!-- /Product Single -->
@endforeach
<div class="clearfix visible-md visible-lg visible-sm visible-xs"></div>
</div>
<!-- /row -->
</div>
<!-- /STORE -->

<!-- end template-->


@endsection

WEB PROGRAMMING III 19


Berikut tampilan halaman detail produk:

Sedangkan untuk filter berdasarkan produk dengan tampilan sebagai berikut:

Ubah menjadi
filter kategori

Lakukan pada pencarian yakni Filter By Brand kemudian ganti menjadi Filter Kategori
dengan script berikut:
<!-- aside widget -->
<div class="aside">
<h3 class="aside-title">Filter Kategori</h3>

WEB PROGRAMMING III 20


<ul class="list-links">
@foreach ($kategori as $row)
<li><a href="{{ route('produk.kategori', $row->id) }}">{{ $row->nama_kategori
}}</a></li>
@endforeach
</ul>
</div>
<!-- /aside widget -->

Sehingga untuk melakukan filter berdasarkan kategori dapat dilakukkan selain dari Menu
Header dan kali ini kita akan sesuaikan halaman sidebar:

<!-- menu nav -->


<div class="menu-nav">
<span class="menu-header">Menu <i class="fa fa-bars"></i></span>
<ul class="menu-list">
<li><a href="{{ route('beranda') }}">Beranda</a></li>
<li><a href="{{ route('produk.all') }}">Produk</a></li>
<li><a href="#">Lokasi</a></li>
<li><a href="#">Hubungi Kami</a></li>
</ul>
</div>
<!-- menu nav -->

WEB PROGRAMMING III 21


Pada controller ProdukController kita tambahkan function produkAll
public function produkAll()
{
$kategori = Kategori::orderBy('nama_kategori', 'desc')->get();
$produk = Produk::where('status', 1)->orderBy('updated_at', 'desc')->paginate(6);
return view('v_produk.index', [
'judul' => 'Semua Produk',
'kategori' => $kategori,
'produk' => $produk,
]);
}

Tambahkan script Pada routes\web.php


Route::get('/produk/all', [ProdukController::class, 'produkAll'])-
>name('produk.all');

Sehingga pada v_produk kita tambahkan file index.blade.php


@extends('v_layouts.app')
@section('content')
<!-- template -->

<!-- STORE -->


<div id="store">
<!-- row -->
<div class="row">
@foreach ($produk as $row)
<!-- Product Single -->
<div class="col-md-4 col-sm-6 col-xs-6">
<div class="product product-single">
<div class="product-thumb">
<div class="product-label">
<span>Kategori</span>
<span class="sale">{{ $row->kategori->nama_kategori }}</span>
</div>

<a href="{{ route('produk.detail', $row->id) }}">


<button class="main-btn quick-view"><i class="fa fa-search-
plus"></i> Detail Produk</button>
</a>
<img src="{{ asset('storage/img-produk/thumb_md_' . $row->foto) }}"
alt="">
</div>
<div class="product-body">

WEB PROGRAMMING III 22


<h3 class="product-price"> Rp. {{ number_format($row->harga, 0, ',',
'.') }} <span
class="product-old-price">{{ $row->kategori->nama_kategori
}}</span></h3>

<h2 class="product-name"><a href="#">{{ $row->nama_produk }}</a></h2>


<div class="product-btns">
<a href="{{ route('produk.detail', $row->id) }}" title="Detail
Produk">
<button class="main-btn icon-btn"><i class="fa fa-search-
plus"></i></button>
</a>
<form action="3" method="post"
style="display: inline-block;" title="Pesan Ke Aplikasi">
@csrf
<button type="submit" class="primary-btn add-to-cart"><i
class="fa fa-shopping-cart"></i> Pesan</button>
</form>
</div>
</div>
</div>
</div>
<!-- /Product Single -->
@endforeach
<div class="clearfix visible-md visible-lg visible-sm visible-xs"></div>

</div>

<!-- Pagination -->


<div class="d-flex justify-content-center mt-4">
{{ $produk->links() }}
</div>
<!-- /Pagination -->

<!-- /row -->


</div>
<!-- /STORE -->

<!-- end template-->


@endsection

Kemudian pada AppServiceProvider.php tambahkan library Pagination sebagai berikut:

WEB PROGRAMMING III 23


Berikut full script pada app.blade.php
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must
come *after* these tags -->

<link rel="icon" type="image/png" sizes="16x16" href="{{


asset('image/icon_univ_bsi.png') }}">
<title>tokoonline</title>

<!-- Google font -->


<link href="https://fonts.googleapis.com/css?family=Hind:400,700" rel="stylesheet">

<!-- Bootstrap -->


<link type="text/css" rel="stylesheet" href="{{ asset('frontend/css/bootstrap.min.css')
}}">

WEB PROGRAMMING III 24


<!-- Slick -->
<link type="text/css" rel="stylesheet" href="{{ asset('frontend/css/slick.css') }}">
<link type="text/css" rel="stylesheet" href="{{ asset('frontend/css/slick-theme.css')
}}">

<!-- nouislider -->


<link type="text/css" rel="stylesheet" href="{{
asset('frontend/css/nouislider.min.css') }}">

<!-- Font Awesome Icon -->


<link rel="stylesheet" href="{{ asset('frontend/css/font-awesome.min.css') }}">

<!-- Custom stlylesheet -->


<link type="text/css" rel="stylesheet" href="{{ asset('frontend/css/style.css') }}">

<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->

</head>

<body>
<!-- HEADER -->
<header>
<!-- top Header -->
<div id="top-header">
<div class="container">
<div class="pull-left">
<span>Selamat datang di toko online</span>
</div>
</div>
</div>
<!-- /top Header -->

<!-- header -->


<div id="header">
<div class="container">
<div class="pull-left">
<!-- Logo -->
<div class="header-logo">
<a class="logo" href="#">
<img src="{{ asset('image/logo.png') }}" alt="">
</a>
</div>
<!-- /Logo -->

<!-- Search -->

<!-- /Search -->


</div>
<div class="pull-right">
<ul class="header-btns">
<!-- Cart -->
<li class="header-cart dropdown default-dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" aria-
expanded="true">
<div class="header-btns-icon">
<i class="fa fa-shopping-cart"></i>
</div>
<strong class="text-uppercase">Keranjang</strong>
</a>
</li>

WEB PROGRAMMING III 25


<!-- /Cart -->

<!-- Account -->


<li class="header-account dropdown default-dropdown">
<div class="dropdown-toggle" role="button" data-
toggle="dropdown" aria-expanded="true">
<div class="header-btns-icon">
<i class="fa fa-user-o"></i>
</div>
<strong class="text-uppercase">Akun Saya<i class="fa fa-
caret-down"></i></strong>
</div>
<a href="#" class="text-uppercase">Login</a>
<ul class="custom-menu">
<li><a href="#"><i class="fa fa-user-o"></i> My
Account</a></li>
<li><a href="#"><i class="fa fa-heart-o"></i> My
Wishlist</a></li>
<li><a href="#"><i class="fa fa-exchange"></i>
Compare</a></li>
<li><a href="#"><i class="fa fa-check"></i>
Checkout</a></li>
<li><a href="#"><i class="fa fa-unlock-alt"></i>
Login</a></li>
<li><a href="#"><i class="fa fa-user-plus"></i> Create An
Account</a></li>
</ul>
</li>
<!-- /Account -->

<!-- Mobile nav toggle-->


<li class="nav-toggle">
<button class="nav-toggle-btn main-btn icon-btn"><i class="fa
fa-bars"></i></button>
</li>
<!-- / Mobile nav toggle -->
</ul>
</div>
</div>
<!-- header -->
</div>
<!-- container -->
</header>
<!-- /HEADER -->

<!-- NAVIGATION -->


<div id="navigation">
<!-- container -->
<div class="container">
<div id="responsive-nav">
@if (request()->segment(1) == '' || request()->segment(1) == 'beranda')
<!-- category nav -->
<div class="category-nav">
<span class="category-header">Kategori <i class="fa fa-
list"></i></span>
<ul class="category-list">
@php
$kategori = DB::table('kategori')->orderBy('nama_kategori', 'asc')-
>get();
@endphp
@foreach ($kategori as $row)
<li><a href="{{ route('produk.kategori', $row->id) }}">{{ $row-
>nama_kategori }}</a></li>
@endforeach
</ul>

WEB PROGRAMMING III 26


<ul class="category-list">
</div>
@else
<div class="category-nav show-on-click">
<span class="category-header">Kategori <i class="fa fa-
list"></i></span>
<ul class="category-list">
@foreach ($kategori as $row)
<li><a href="{{ route('produk.kategori', $row->id) }}">{{ $row-
>nama_kategori }}</a></li>
@endforeach
</ul>
</div>
<!-- /category nav -->
@endif
<!-- menu nav -->
<div class="menu-nav">
<span class="menu-header">Menu <i class="fa fa-bars"></i></span>
<ul class="menu-list">
<li><a href="{{ route('beranda') }}">Beranda</a></li>
<li><a href="{{ route('produk.all') }}">Produk</a></li>
<li><a href="#">Lokasi</a></li>
<li><a href="#">Hubungi Kami</a></li>

</ul>
</div>
<!-- menu nav -->
</div>
</div>
<!-- /container -->
</div>
<!-- /NAVIGATION -->

@if (request()->segment(1) == '' || request()->segment(1) == 'beranda')


<!-- HOME -->
<div id="home">
<!-- container -->
<div class="container">
<!-- home wrap -->
<div class="home-wrap">
<!-- home slick -->
<div id="home-slick">
<!-- banner -->
<div class="banner banner-1">
<img src="{{ asset('frontend/banner/banner01.jpg') }}" alt="">
<div class="banner-caption text-center">
<h1>Jajanan Tradisional</h1>
<h3 class="font-weak" style="color: 30323a;">Khas Makanan
Indonesia</h3>
<button class="primary-btn">Pesan Sekarang</button>
</div>
</div>
<!-- /banner -->

<!-- banner -->


<div class="banner banner-1">
<img src="{{ asset('frontend/banner/banner02.jpg') }}" alt="">
<div class="banner-caption">
<h1 class="primary-color">Khas Makanan Indonesia<br><span
class="white-color font-weak">Jajanan Tradisional</span></h1>
<button class="primary-btn">Pesan Sekarang</button>
</div>
</div>
<!-- /banner -->

<!-- banner -->

WEB PROGRAMMING III 27


<div class="banner banner-1">
<img src="{{ asset('frontend/banner/banner03.jpg') }}" alt="">
<div class="banner-caption">
<h1 style="color: f8694a;">Khas Makanan
<span>Indonesia</span></h1>
<button class="primary-btn">Pesan Sekarang</button>
</div>
</div>
<!-- /banner -->
</div>
<!-- /home slick -->
</div>
<!-- /home wrap -->
</div>
<!-- /container -->
</div>
<!-- /HOME -->
@endif

<!-- section -->


<div class="section">
<!-- container -->
<div class="container">
<!-- row -->
<div class="row">
<!-- ASIDE -->
<div id="aside" class="col-md-3">
<!-- aside widget -->
<div class="aside">
<h3 class="aside-title">Top Rated Product</h3>
<!-- widget product -->
<div class="product product-widget">
<div class="product-thumb">
<img src="{{ asset('frontend/img/thumb-product01.jpg') }}"
alt="">
</div>
<div class="product-body">
<h2 class="product-name"><a href="#">Product Name Goes
Here</a></h2>
<h3 class="product-price">$32.50 <del class="product-old-
price">$45.00</del></h3>
<div class="product-rating">
<i class="fa fa-star"></i>
<i class="fa fa-star"></i>
<i class="fa fa-star"></i>
<i class="fa fa-star"></i>
<i class="fa fa-star-o empty"></i>
</div>
</div>
</div>
<!-- /widget product -->

<!-- widget product -->


<div class="product product-widget">
<div class="product-thumb">
<img src="{{ asset('frontend/img/thumb-product01.jpg') }}"
alt="">
</div>
<div class="product-body">
<h2 class="product-name"><a href="#">Product Name Goes
Here</a></h2>
<h3 class="product-price">$32.50</h3>
<div class="product-rating">
<i class="fa fa-star"></i>

WEB PROGRAMMING III 28


<i class="fa fa-star"></i>
<i class="fa fa-star"></i>
<i class="fa fa-star"></i>
<i class="fa fa-star-o empty"></i>
</div>
</div>
</div>
<!-- /widget product -->
</div>
<!-- /aside widget -->
<!-- aside widget -->
<div class="aside">
<h3 class="aside-title">Filter Kategori</h3>
<ul class="list-links">
@foreach ($kategori as $row)
<li><a href="{{ route('produk.kategori', $row->id) }}">{{ $row-
>nama_kategori }}</a></li>
@endforeach
</ul>
</div>
<!-- /aside widget -->
</div>
<!-- /ASIDE -->

<!-- MAIN -->


<div id="main" class="col-md-9">
<!-- store top filter -->
<!-- /store top filter -->

<!-- @yieldAwal -->


@yield('content')
<!-- @yieldAkhir-->

<!-- store bottom filter -->

<!-- /store bottom filter -->


</div>
<!-- /MAIN -->
</div>
<!-- /row -->
</div>
<!-- /container -->
</div>
<!-- /section -->

<!-- FOOTER -->


<footer id="footer" class="section section-grey">
<!-- container -->
<div class="container">
<!-- row -->
<div class="row">
<!-- footer widget -->
<div class="col-md-3 col-sm-6 col-xs-6">
<div class="footer">
<!-- footer logo -->
<div class="footer-logo">
<a class="logo" href="#">
<img src="./img/logo.png" alt="">
</a>
</div>
<!-- /footer logo -->

<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do


eiusmod tempor incididunt ut labore et dolore magna</p>

<!-- footer social -->

WEB PROGRAMMING III 29


<ul class="footer-social">
<li><a href="#"><i class="fa fa-facebook"></i></a></li>
<li><a href="#"><i class="fa fa-twitter"></i></a></li>
<li><a href="#"><i class="fa fa-instagram"></i></a></li>
<li><a href="#"><i class="fa fa-google-plus"></i></a></li>
<li><a href="#"><i class="fa fa-pinterest"></i></a></li>
</ul>
<!-- /footer social -->
</div>
</div>
<!-- /footer widget -->

<!-- footer widget -->


<div class="col-md-3 col-sm-6 col-xs-6">
<div class="footer">
<h3 class="footer-header">My Account</h3>
<ul class="list-links">
<li><a href="#">My Account</a></li>
<li><a href="#">My Wishlist</a></li>
<li><a href="#">Compare</a></li>
<li><a href="#">Checkout</a></li>
<li><a href="#">Login</a></li>
</ul>
</div>
</div>
<!-- /footer widget -->

<div class="clearfix visible-sm visible-xs"></div>

<!-- footer widget -->


<div class="col-md-3 col-sm-6 col-xs-6">
<div class="footer">
<h3 class="footer-header">Customer Service</h3>
<ul class="list-links">
<li><a href="#">About Us</a></li>
<li><a href="#">Shiping & Return</a></li>
<li><a href="#">Shiping Guide</a></li>
<li><a href="#">FAQ</a></li>
</ul>
</div>
</div>
<!-- /footer widget -->

<!-- footer subscribe -->


<div class="col-md-3 col-sm-6 col-xs-6">
<div class="footer">
<h3 class="footer-header">Stay Connected</h3>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
eiusmod tempor.</p>
<form>
<div class="form-group">
<input class="input" placeholder="Enter Email Address">
</div>
<button class="primary-btn">Join Newslatter</button>
</form>
</div>
</div>
<!-- /footer subscribe -->
</div>
<!-- /row -->
<hr>
<!-- row -->
<div class="row">
<div class="col-md-8 col-md-offset-2 text-center">
<!-- footer copyright -->
<div class="footer-copyright">

WEB PROGRAMMING III 30


<!-- Link back to Colorlib can't be removed. Template is licensed
under CC BY 3.0. -->
Copyright &copy;<script>
document.write(new Date().getFullYear());
</script> All rights reserved | This template is made with <i
class="fa fa-heart-o" aria-hidden="true"></i> by <a href="https://colorlib.com"
target="_blank">Colorlib</a>
<!-- Link back to Colorlib can't be removed. Template is licensed
under CC BY 3.0. -->
</div>
<!-- /footer copyright -->
</div>
</div>
<!-- /row -->
</div>
<!-- /container -->
</footer>
<!-- /FOOTER -->

<!-- jQuery Plugins -->


<script src="{{ asset('frontend/js/jquery.min.js') }}"></script>
<script src="{{ asset('frontend/js/bootstrap.min.js') }}"></script>
<script src="{{ asset('frontend/js/slick.min.js') }}"></script>
<script src="{{ asset('frontend/js/nouislider.min.js') }}"></script>
<script src="{{ asset('frontend/js/jquery.zoom.min.js') }}"></script>
<script src="{{ asset('frontend/js/main.js') }}"></script>

</body>

</html>

WEB PROGRAMMING III 31


Minggu Ke-3
Registrasi dan Login Member dengan API Google

API Google adalah sekumpulan antarmuka pemrograman aplikasi (Application Programming


Interface) yang disediakan oleh Google untuk memungkinkan pengembang mengintegrasikan
layanan dan fitur Google ke dalam aplikasi mereka. API Google menyediakan akses ke
berbagai layanan Google, seperti Google Maps, Gmail, Google Drive, YouTube, Google
Calendar, dan banyak lagi. Dengan menggunakan API Google, pengembang dapat
memanfaatkan fungsionalitas yang sudah ada dari layanan Google tanpa perlu membangunnya
dari awal, sehingga menghemat waktu dan sumber daya.

API Google bekerja dengan cara mengirimkan permintaan (request) dari aplikasi ke server
Google, dan server Google akan mengirimkan respons (response) yang berisi data atau hasil
dari layanan yang diminta. Misalnya, Google Maps API dapat digunakan untuk menampilkan
peta di dalam aplikasi, atau Google Drive API dapat dimanfaatkan untuk memungkinkan
pengguna menyimpan dan mengakses file mereka di Google Drive langsung dari aplikasi.

Google Client ID dan Client Secret adalah dua komponen penting yang diberikan oleh Google
Developers Console untuk mengidentifikasi dan mengamankan aplikasi saat berinteraksi
dengan layanan Google. Client ID berfungsi sebagai identifier unik yang digunakan untuk
mengenali aplikasi yang meminta akses ke data pengguna melalui OAuth 2.0. Sedangkan
Client Secret adalah kode rahasia yang bertindak sebagai bukti bahwa permintaan yang
dikirim ke Google berasal dari aplikasi yang sah. Keduanya digunakan bersama dalam proses
autentikasi dan otorisasi, seperti memungkinkan pengguna login dengan akun Google atau
mengakses data pengguna dari layanan Google seperti Gmail atau Google Drive.

Contoh Client ID:


1234567890-abcdefgh.apps.googleusercontent.com

Contoh Client Secret:


GOCSPX-abcdefgh1234567890ijklmnop

Proses kerja dimulai ketika aplikasi mengarahkan pengguna ke halaman login Google dengan
menyertakan Client ID. Setelah pengguna berhasil login dan memberikan izin, Google akan
mengirimkan kode otorisasi ke aplikasi melalui Authorized Redirect URI. Selanjutnya, aplikasi
menukar kode otorisasi tersebut dengan access token menggunakan Client ID dan Client
Secret. Access token inilah yang memungkinkan aplikasi untuk mengakses data pengguna dari
layanan Google.

WEB PROGRAMMING III 32


1. Untuk mendapatkan client ID dan client secret, maka kita dapat berkunjung ke google cloud
platform terlebih dahulu https://cloud.google.com selanjutnya masuk menggunakan akun
google, setelah itu masuk ke dalam konsol

2. Pilih My Project

WEB PROGRAMMING III 33


3. Pilih New Project

4. Pada Project Name, misalnya tokoonline-laravel3. Kemudian klik Create

WEB PROGRAMMING III 34


5. Kemudian pilih Project yang baru kita buat yakni tokoonline-laravel3

6. Kali ini, project telah berganti ke project yang dituju, yaitu tokoonline-laravel3. Kemudian
klik menu pada sebelah kiri project.

WEB PROGRAMMING III 35


7. Pilih APIs & Services -> OAuth Consent Screen

8. Pilih Get Started

WEB PROGRAMMING III 36


9. pada User support email masukkan alamat email yang diakan digunakan, kemudian klik
Next

10. pilih External, kemudian Next

WEB PROGRAMMING III 37


11. pada Contact Information, misalnya mengisi kembali alamat email seperti pada nomor 9
dan kemudian klik Next

12. Ceklist I Agree ->Continue->Create

WEB PROGRAMMING III 38


13. pada Contact Information, misalnya mengisi kembali alamat email seperti pada nomor 9,
sedangkan untuk Application home pakai karena masih bersifat local maka kita isikan
http://127.0.0.1:8000 dan kemudian klik Next

14. Kemudian pada menu Clients –> Create Client

WEB PROGRAMMING III 39


15. Kemudian kita pilih Web appplicationpada URLs 1:
http://127.0.0.1:8000/auth/google/callback kemudian klik tombol Create

16. Klik Web client 1 untuk mendapatkan informasi seperti Client ID & Client Secrets

WEB PROGRAMMING III 40


17. copy Client ID & Client Secrets

18. paste Client ID & Client Secrets pada env.


# API Google
GOOGLE_CLIENT_ID= [masukkan Client ID masing-masing]
GOOGLE_CLIENT_SECRET=[masukkan Client Secrets masing-masing]
GOOGLE_REDIRECT_URL=http://127.0.0.1:8000/auth/google/callback

19. kemudian pada config/services.php:


'google' => [
'client_id' => env('GOOGLE_CLIENT_ID'),
'client_secret' => env('GOOGLE_CLIENT_SECRET'),
'redirect' => env('GOOGLE_REDIRECT_URL'),
],

WEB PROGRAMMING III 41


20. Berikutnya yakni kita buatkan blueprint customer
php artisan make:migration create_customer_table
Schema::create('customer', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('user_id');
$table->string('google_id')->nullable();
$table->string('google_token')->nullable();
$table->string('alamat')->nullable();
$table->string('pos')->nullable();
$table->timestamps();
$table->foreign('user_id')->references('id')->on('user')->onDelete('cascade');
});

Dan pastikan terdapat tabel user dengan blueprint sebagai berikut:


Schema::create('user', function (Blueprint $table) {
$table->id();
$table->string('nama');
$table->string('email')->unique();
$table->enum('role', [0, 1, 2])->default(0); // 0 = Admin, 1 = SuperAdmin,
2=customer
$table->boolean('status'); // 0 = Belum aktif, 1=Aktif
$table->string('password');
$table->string('hp', 13)->nullable();
$table->string('foto')->nullable();
$table->timestamps();
});

Jika sudah yakin maka jalankan perintah


php artisan migrate

21. Buat model dengan nama Customer


php artisan make:model Customer

<?php

namespace App\Models;

WEB PROGRAMMING III 42


// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;

class Customer extends Authenticatable


{
use HasApiTokens, HasFactory, Notifiable;

/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $table = "customer";
protected $fillable = [
'user_id',
'google_id',
'google_token',
'alamat',
'pos',
];

public function user()


{
return $this->belongsTo(User::class, 'user_id', 'id');
}
}

22. Membuat Contoller dengan nama CustomerController.php


php artisan make:controller CustomerController

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Customer;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Laravel\Socialite\Facades\Socialite;
use Illuminate\Support\Facades\Hash;

class CustomerController extends Controller


{
// Redirect ke Google
public function redirect()
{
return Socialite::driver('google')->redirect();
}

// Callback dari Google


public function callback()
{
try {
$socialUser = Socialite::driver('google')->user();

// Cek apakah email sudah terdaftar


$registeredUser = User::where('email', $socialUser->email)->first();

if (!$registeredUser) {
// Buat user baru
$user = User::create([

WEB PROGRAMMING III 43


'nama' => $socialUser->name,
'email' => $socialUser->email,
'role' => '2', // Role customer
'status' => 1, // Status aktif
'password' => Hash::make('default_password'), // Password default
(opsional)
]);

// Buat data customer


Customer::create([
'user_id' => $user->id,
'google_id' => $socialUser->id,
'google_token' => $socialUser->token
]);

// Login pengguna baru


Auth::login($user);
} else {
// Jika email sudah terdaftar, langsung login
Auth::login($registeredUser);
}

// Redirect ke halaman utama


return redirect()->intended('beranda');
} catch (\Exception $e) {
// Redirect ke halaman utama jika terjadi kesalahan
return redirect('/')->with('error', 'Terjadi kesalahan saat login dengan
Google.');
}
}

public function logout(Request $request)


{
Auth::logout(); // Logout pengguna
$request->session()->invalidate(); // Hapus session
$request->session()->regenerateToken(); // Regenerate token CSRF

return redirect('/')->with('success', 'Anda telah berhasil logout.');


}
}

Tambahkan liberary Socialite pada terminal:


composer require laravel/socialite

23. Tambahkan script Pada routes\web.php


use App\Http\Controllers\CustomerController;

//API Google
Route::get('/auth/redirect', [CustomerController::class, 'redirect'])-
>name('auth.redirect');

WEB PROGRAMMING III 44


Route::get('/auth/google/callback', [CustomerController::class, 'callback'])-
>name('auth.callback');
// Logout
Route::post('/logout', [CustomerController::class, 'logout'])->name('logout');

24. Pada App pada direktori resources/views/v_layouts/App.blade.php dengan pencarian


yakni login.
<a href="{{ route('auth.redirect') }}" class="text-uppercase">Login</a>

25. Sehingga pada saat klik tombol Login meminta akun gmail, pilih gmail masing-masing.
Pastikan gmail belum pernah digunakan pada tabel user.

Klik Lanjut

WEB PROGRAMMING III 45


Maka akan tampil kehalaman utama

WEB PROGRAMMING III 46


Untuk memastikan kembali apakah registrasi berhasil dilakukan kita bisa cek untuk sementara
melalu phpmyadmin:

Sebagai contoh, akun Gmail [email protected] berhasil tersimpan pada tabel user dengan
detail sebagai berikut: id = 3, nama = Sopian Aji, email = [email protected], role = 2, dan
password yang telah terenkripsi. Selanjutnya, periksa tabel customer untuk memastikan
apakah id = 3 sudah ditambahkan ke dalam tabel tersebut. Dan pada tabel customer sudah
berhasil tersimpan dengan baik.

WEB PROGRAMMING III 47


Minggu Ke-4
Mengelola Manajemen Akun User

Manajemen akun pengguna mencakup pengelolaan dari dua sisi, yaitu frontend dan backend.
Pada sisi frontend, pengguna umum dapat mengakses dan menggunakan aplikasi, sementara
pada sisi backend, pengelola aplikasi dapat memantau serta mengelola data pelanggan. Dengan
demikian, pengelolaan akun dilakukan secara dua arah.

1. Pengaturan pada Frontend


a. kita akan mengatur dari segit fronted terlebih dulu, dimana kita akan memperbaiki aplikasi
agar dapat menampilkan informasi pengguna yang sedang login menggunakan session,
sehingga pengecekan tidak perlu dilakukan secara manual melalui phpMyAdmin. Lakukan
perubahan pada aplikasi di direktori resources/views/v_layouts/app.blade.php, lalu tambahkan
pemanggilan session untuk menampilkan informasi pengguna yang sedang login. Selanjutnya,
cari komentar <!-- Account --> untuk melakukan penyesuaian. Dengan script sebelumnya
sebagai berikut:
<!-- Account -->
<li class="header-account dropdown default-dropdown">
<div class="dropdown-toggle" role="button" data-toggle="dropdown" aria-expanded="true">
<div class="header-btns-icon">
<i class="fa fa-user-o"></i>
</div>
<strong class="text-uppercase">Akun Saya<i class="fa fa-caret-down"></i></strong>
</div>
<a href="#" class="text-uppercase">Login</a>
<ul class="custom-menu">
<li><a href="#"><i class="fa fa-user-o"></i> My Account</a></li>
<li><a href="#"><i class="fa fa-heart-o"></i> My Wishlist</a></li>
<li><a href="#"><i class="fa fa-exchange"></i> Compare</a></li>
<li><a href="#"><i class="fa fa-check"></i> Checkout</a></li>
<li><a href="#"><i class="fa fa-unlock-alt"></i> Login</a></li>
<li><a href="#"><i class="fa fa-user-plus"></i> Create An Account</a></li>
</ul>
</li>
<!-- /Account -->

Menjadi:

<!-- Account -->


@if (Auth::check())
<li class="header-account dropdown default-dropdown">
<div class="dropdown-toggle" role="button" data-toggle="dropdown" aria-expanded="true">
<div class="header-btns-icon">
<i class="fa fa-user-o"></i>
</div>
<strong class="text-uppercase">{{ Auth::user()->nama }}<i
class="fa fa-caret-down"></i></strong>
</div>
<ul class="custom-menu">
<li><a href="#"><i class="fa fa-user-o"></i> Akun Saya</a></li>
<li><a href="#"><i class="fa fa-check"></i> History</a></li>
<li>
<a href="#"
onclick="event.preventDefault(); document.getElementById('keluar-
app').submit();"><i class="fa fa-power-off"></i> Keluar
</a>
<!-- form keluar app -->
<form id="keluar-app" action="{{ route('logout') }}" method="POST" class="d-
none">

WEB PROGRAMMING III 48


@csrf
</form>
<!-- form keluar app end -->
</li>
</ul>
</li>
@else
<li class="header-account dropdown default-dropdown">
<div class="dropdown-toggle" role="button" data-toggle="dropdown" aria-expanded="true">
<div class="header-btns-icon">
<i class="fa fa-user-o"></i>
</div>
<strong class="text-uppercase">Akun Saya<i class="fa fa-caret-down"></i></strong>
</div>
<a href="{{ route('auth.redirect') }}" class="text-uppercase">Login</a>
</li>
@endif
<!-- /Account -->

b. Halaman ini dalam keadaan belum login

c. Halaman dalam keadaan sudah login, dengan tombol Keluar yang dapat digunakan untuk
keluar dari aplikasi.

2. Pengaturan pada Backend


a. Pertama kita tambahkan sidebar pada backend yakni pada
resources/views/backend/v_layouts/app.blade.php dengan pencarian pada komentar <!--
Sidebar navigation-->

WEB PROGRAMMING III 49


<li class="sidebar-item"> <a class="sidebar-link waves-effect waves-dark sidebar-link"
href="{{ route('backend.customer.index') }}" aria-expanded="false"><i class="mdi mdi-
account-multiple"></i><span class="hide-menu">Customer</span></a>
</li>

b. tambahkan pada CustomerController:


public function index()
{
$customer = Customer::orderBy('id', 'desc')->get();
return view('backend.v_customer.index', [
'judul' => 'Customer',
'sub' => 'Halaman Customer',
'index' => $customer
]);
}

c. Tambahkan script Pada routes\web.php


// Route untuk Customer
Route::resource('backend/customer', CustomerController::class, ['as' => 'backend'])-
>middleware('auth');

d. backend/v_customer/index.blade.php, berikut script lengkapnya:


@extends('backend.v_layouts.app')
@section('content')
<!-- contentAwal -->

<div class="row">
<div class="col-12">
<div class="card">
<div class="card-body">

<h5 class="card-title">{{$judul}} <br><br>


</h5>

<div class="table-responsive">
<table id="zero_config" class="table table-striped table-bordered">
<thead>
<tr>
<th>No</th>
<th>Nama</th>
<th>Email</th>
<th>Aksi</th>
</tr>
</thead>
<tbody>
@foreach ($index as $row)
<tr>
<td> {{ $loop->iteration }} </td>
<td> {{$row->user->nama}} </td>
<td> {{$row->user->email}} </td>
<td>
<a href="#" title="Ubah Data">
<button type="button" class="btn btn-warning btn-
sm"><i class="fas fa-eye"></i> Detail</button>
</a>
<a href="#" title="Ubah Data">
<button type="button" class="btn btn-cyan btn-
sm"><i class="far fa-edit"></i> Ubah</button>
</a>

WEB PROGRAMMING III 50


<form method="POST" action="#" style="display: inline-
block;">
@method('delete')
@csrf
<button type="submit" class="btn btn-danger btn-sm
show_confirm" data-konf-delete="{{ $row->nama}}" title='Hapus Data'>
<i class="fas fa-trash"></i> Hapus</button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>

</div>
</div>
</div>
</div>

<!-- contentAkhir -->


@endsection

e. sehingga halaman customer pada backend sebagai berikut:

Silakan jadikan aksi seperti Detail, Ubah, dan Hapus sebagai tugas mandiri untuk dilengkapi,
sehingga halaman backend dapat mengelola data dengan lebih baik.

WEB PROGRAMMING III 51


Minggu Ke-5
Route Group dan Middleware

Sebelum masuk ke sesi praktik, penting untuk memahami dua konsep utama dalam Laravel
yang sering digunakan untuk mengelola rute dan keamanan aplikasi, yaitu Route Group dan
Middleware

Route Group adalah fitur di Laravel yang memungkinkan kita mengelompokkan beberapa
rute dengan konfigurasi yang sama, seperti prefix, middleware, atau namespace. Dengan
Route Group, kode menjadi lebih rapi dan mudah dikelola, terutama saat bekerja dengan
banyak rute.

Misalnya, jika kita memiliki beberapa rute dalam halaman admin, kita bisa
mengelompokkannya dalam satu grup dengan prefix /admin, sehingga semua rute di dalamnya
otomatis memiliki awalan admin/ tanpa perlu menulisnya satu per satu.

Middleware adalah lapisan keamanan dalam Laravel yang digunakan untuk memproses
permintaan (request) sebelum mencapai aplikasi. Middleware sering digunakan untuk
autentikasi, otorisasi, log aktivitas, atau pengolahan data sebelum dikirim ke aplikasi.

Contoh umum penggunaan Middleware adalah memastikan bahwa hanya pengguna yang
sudah login yang dapat mengakses halaman admin. Laravel menyediakan middleware bawaan
seperti auth, tetapi kita juga bisa membuat middleware sendiri sesuai kebutuhan.

Setelah memahami konsep ini, kita akan langsung masuk ke praktik bagaimana menerapkan
Route Group dan Middleware dalam Laravel untuk meningkatkan efisiensi dan keamanan
aplikasi.

1. Membuat Middleware, misalnya untuk menerapkan sistem keamanan pada customer. Di


terminal, kita akan membuat middleware dengan nama IsCustomer:

php artisan make:middleware IsCustomer

WEB PROGRAMMING III 52


2. Pada IsCustomer yakni pada direktori Http\Middleware\IsCustomer kita tambahkan script
sebagai berikut:
public function handle(Request $request, Closure $next): Response
{
if (auth()->check() && auth()->user()->role == 2) {
return $next($request);
}

return redirect('/auth/redirect')->with('msgError', 'Anda harus login sebagai


customer');
}

3. Kemudian daftarkan middleware IsCustomer dpada direktori app/Http/Kernel.php.


Tambahkan middleware ke dalam properti protected $middlewareAliases:
'is.customer' => \App\Http\Middleware\IsCustomer::class,

4. Pada Controller yakni CustomerController sebagai berikut:


use App\Helpers\ImageHelper;

public function akun($id)


{
$loggedInCustomerId = Auth::user()->id;
// Cek apakah ID yang diberikan sama dengan ID customer yang sedang login
if ($id != $loggedInCustomerId) {
// Redirect atau tampilkan pesan error
return redirect()->route('customer.akun', ['id' => $loggedInCustomerId])-
>with('msgError', 'Anda tidak berhak mengakses akun ini.');
}
$customer = Customer::where('user_id', $id)->firstOrFail();
return view('v_customer.edit', [
'judul' => 'Customer',
'subJudul' => 'Akun Customer',
'edit' => $customer
]);
}

public function updateAkun(Request $request, $id)


{
$customer = Customer::where('user_id', $id)->firstOrFail();
$rules = [
'nama' => 'required|max:255',
'hp' => 'required|min:10|max:13',
'foto' => 'image|mimes:jpeg,jpg,png,gif|file|max:1024',
];
$messages = [
'foto.image' => 'Format gambar gunakan file dengan ekstensi jpeg, jpg, png, atau
gif.',
'foto.max' => 'Ukuran file gambar Maksimal adalah 1024 KB.'

WEB PROGRAMMING III 53


];

if ($request->email != $customer->user->email) {
$rules['email'] = 'required|max:255|email|unique:customer';
}
if ($request->alamat != $customer->alamat) {
$rules['alamat'] = 'required';
}
if ($request->pos != $customer->pos) {
$rules['pos'] = 'required';
}

$validatedData = $request->validate($rules, $messages);


// menggunakan ImageHelper
if ($request->file('foto')) {
//hapus gambar lama
if ($customer->user->foto) {
$oldImagePath = public_path('storage/img-customer/') . $customer->user->foto;
if (file_exists($oldImagePath)) {
unlink($oldImagePath);
}
}
$file = $request->file('foto');
$extension = $file->getClientOriginalExtension();
$originalFileName = date('YmdHis') . '_' . uniqid() . '.' . $extension;
$directory = 'storage/img-customer/';
// Simpan gambar dengan ukuran yang ditentukan
ImageHelper::uploadAndResize($file, $directory, $originalFileName, 385, 400); //
null (jika tinggi otomatis)
// Simpan nama file asli di database
$validatedData['foto'] = $originalFileName;
}

$customer->user->update($validatedData);

$customer->update([
'alamat' => $request->input('alamat'),
'pos' => $request->input('pos'),
]);
return redirect()->route('customer.akun', $id)->with('success', 'Data berhasil
diperbarui');
}

Jangan lupa untuk menambahkan folder img-customer pada direktori storage/app/public/ img-
customer

5. Tambahkan script Pada routes\web.php untuk menterjemahkan function detail pada


CustomerController:
// Route untuk menampilkan halaman akun customer
Route::get('/customer/akun/{id}', [CustomerController::class, 'akun'])-
>name('customer.akun')->middleware('is.customer');
Route::put('/customer/update/{id}', [CustomerController::class, 'updateAkun'])-
>name('customer.update')->middleware('is.customer');

6. pada App pada direktori resources/views/v_layouts/app.blade.php dengan pencarian pada


komentar <!-- Account -->
<li><a href="{{ route('customer.akun', ['id' => Auth::user()->id]) }}"><i class="fa fa-
user-o"></i> Akun Saya</a>
</li>

WEB PROGRAMMING III 54


Sehingga saat Akun Saya disentuh url sudah terbaca

7. Kemudian kita buatkan tampilan viewnya, yakni buat folder dengan nama v_customer
dengan direktori sebagai berikut resources/views/v_customer/edit.blade.php

@extends('v_layouts.app')
@section('content')
<!-- template -->

<div class="row">
<div class="col-md-12">
<div class=" billing-details">
<div class="section-title">
<h3 class="title"> {{$judul}} </h3>
</div>
<div class="row">
<div class="col-md-12">
<!-- msgError -->
@if(session()->has('success'))
<div class="alert alert-success alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-
label="Close"><span aria-hidden="true">&times;</span></button>
<strong>{{ session('success') }}</strong>
</div>
@endif
<!-- end msgError -->
<!-- msgError -->
@if(session()->has('msgError'))
<div class="alert alert-danger alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-
label="Close"><span aria-hidden="true">&times;</span></button>
<strong>{{ session('msgError') }}</strong>
</div>
@endif
<!-- end msgError -->
</div>
<form action="{{ route('customer.akun, $edit->user->id) }}" method="post"
enctype="multipart/form-data">
@method('put')
@csrf

WEB PROGRAMMING III 55


<div class="col-md-4">
<div class="form-group">
<label>Foto</label>
{{-- view image --}}
@if ($edit->foto)
<img src="{{ asset('storage/img-customer/' . $edit->user->foto)
}}" class="foto-preview" width="100%">
<p></p>
@else
<img src="{{ asset('storage/img-user/img-default.jpg') }}"
class="foto-preview" width="100%">
<p></p>
@endif
{{-- file foto --}}
<input type="file" name="foto" class="form-control
@error('foto') is-invalid @enderror" onchange="previewFoto()">
@error('foto')
<div class="invalid-feedback alert-danger">{{ $message }}</div>
@enderror
</div>
</div>
<div class="col-md-8">

<div class="form-group">
<label>Nama</label>
<input type="text" name="nama" value="{{ old('nama', $edit-
>user->nama) }}" class="form-control @error('nama') is-invalid @enderror"
placeholder="Masukkan Nama">
@error('nama')
<span class="invalid-feedback alert-danger" role="alert">
{{ $message }}
</span>
@enderror
</div>

<div class="form-group">
<label>Email</label>
<input type="text" name="email" value="{{ old('email', $edit-
>user->email) }}" class="form-control @error('email') is-invalid @enderror"
placeholder="Masukkan Email">
@error('email')
<span class="invalid-feedback alert-danger" role="alert">
{{ $message }}
</span>
@enderror
</div>

<div class="form-group">
<label>HP</label>
<input type="text" onkeypress="return hanyaAngka(event)"
name="hp" value="{{ old('hp', $edit->user->hp) }}" class="form-control @error('hp') is-
invalid @enderror" placeholder="Masukkan Nomor HP">
@error('hp')
<span class="invalid-feedback alert-danger" role="alert">
{{ $message }}
</span>
@enderror
</div>
<div class="form-group">
<label>Alamat</label><br>
<textarea name="alamat" class="form-control @error('alamat')
is-invalid @enderror">{{ old('alamat',$edit->alamat) }}</textarea>
@error('alamat')
<span class="invalid-feedback alert-danger" role="alert">
{{ $message }}

WEB PROGRAMMING III 56


</span>
@enderror
</div>

<div class="form-group">
<label>Kode Pos</label>
<input type="text" name="pos" value="{{ old('pos', $edit->pos)
}}" class="form-control @error('pos') is-invalid @enderror" placeholder="Masukkan Nomor
Resi">
@error('pos')
<span class="invalid-feedback alert-danger" role="alert">
{{ $message }}
</span>
@enderror
</div>
</div>

<br>
<div class="col-md-12">
<br>
<div class="pull-left">
<button type="submit" class="primary-btn">Simpan</button>
</div>
</div>

</form>
</div>
</div>
</div>
</div>
</div>

<!-- end template-->


@endsection

Berikut halaman Akun Customer:

WEB PROGRAMMING III 57


8. Sebelumnya script Pada routes\web.php sebagai berikut:
// Route untuk menampilkan halaman akun customer
Route::get('/customer/akun/{id}', [CustomerController::class, 'akun'])-
>name('customer.akun')->middleware('is.customer');
Route::put('/customer/akun/{id}/update', [CustomerController::class, 'updateAkun'])-
>name('customer.akun.update')->middleware('is.customer');

Kali ini kita akan menggunakan group, sehingga kita dapat mengelompokkan route yang
terkait dengan customer menjadi sebagai berikut

// Group route untuk customer


Route::middleware('is.customer')->group(function () {
// Route untuk menampilkan halaman akun customer
Route::get('/customer/akun/{id}', [CustomerController::class, 'akun'])
->name('customer.akun');

// Route untuk mengupdate data akun customer


Route::put('/customer/updateakun/{id}', [CustomerController::class, 'updateAkun'])
->name('customer.updateakun');
});

WEB PROGRAMMING III 58


Minggu Ke-6
Manajemen Keranjang Belanja

Keranjang belanja (shopping cart) adalah fitur penting dalam aplikasi e-commerce yang
memungkinkan pengguna menyimpan produk yang ingin dibeli sebelum melakukan transaksi.
Manajemen keranjang belanja mencakup berbagai aspek, seperti menambah, mengupdate, dan
menghapus produk dalam keranjang, serta menghitung total harga secara otomatis.

Teknologi yang Digunakan


• Sesi (Session): Menyimpan data sementara di sisi server
• Database: Menyimpan data secara permanen agar pengguna tetap bisa melihat keranjang
• AJAX & JavaScript: Memungkinkan pembaruan keranjang secara real-time tanpa reload
halaman.

Manajemen keranjang belanja yang baik akan meningkatkan pengalaman pengguna serta
memastikan proses transaksi berjalan dengan lancar. Sekarang, mari kita masuk ke pembahasan
lebih lanjut mengenai implementasi fitur ini

1. Pertama, yang akan kita lakukan adalah membuat tabel order dan order_item untuk
menyimpan data proses transaksi. Membuat tabel order sebagai barikut:
php artisan make:migration create_order_table

dengan blueprint sebagai barikut:


Schema::create('order', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('customer_id');
$table->unsignedBigInteger('user_id')->nullable();
$table->string('status');
$table->string('noresi')->nullable();
$table->string('kurir')->nullable();
$table->string('layanan_ongkir')->nullable();
$table->string('biaya_ongkir')->nullable();
$table->string('estimasi_ongkir')->nullable();
$table->integer('total_berat')->nullable();
$table->double('total_harga');
$table->text('alamat')->nullable();
$table->string('pos')->nullable();
$table->timestamps();
$table->foreign('customer_id')->references('id')->on('customer')-
>onDelete('cascade');
$table->foreign('user_id')->references('id')->on('user')->onDelete('cascade');
});

Tabel berikutnya order_item, sebagai berikut:


php artisan make:migration create_order_item_table

Schema::create('order_item', function (Blueprint $table) {


$table->id();
$table->unsignedBigInteger('order_id');
$table->unsignedBigInteger('produk_id');
$table->integer('quantity');
$table->double('harga');
$table->timestamps();
$table->foreign('order_id')->references('id')->on('order')-
>onDelete('cascade');
$table->foreign('produk_id')->references('id')->on('produk')-
>onDelete('cascade');

WEB PROGRAMMING III 59


});

Kemudian jalankan migrate:

2. sehingga relasi tabel pada tokoonline sebagai barikut:

WEB PROGRAMMING III 60


3. Membuat model dengan nama Order dan OrderItem
php artisan make:model Order
php artisan make:model OrderItem

Model Order:
class Order extends Model
{
public $timestamps = true;
protected $table = "order";
protected $fillable = [
'customer_id',
'total_harga',
'status',
'noresi',
'kurir',
'layanan_ongkir',
'biaya_ongkir',
'estimasi_ongkir',
'total_berat',
'alamat',
'pos'
];

public function orderItems()


{
return $this->hasMany(OrderItem::class);
}

public function customer()


{
return $this->belongsTo(Customer::class);
}
}

Model OrderItem:
class OrderItem extends Model
{
public $timestamps = true;
protected $table = "order_item";
protected $fillable = ['order_id', 'produk_id', 'quantity', 'harga'];

public function order()


{
return $this->belongsTo(Order::class);
}

public function produk()


{
return $this->belongsTo(Produk::class);
}

public function kategori()


{
return $this->belongsTo(Kategori::class);
}

WEB PROGRAMMING III 61


}

3. Membuat controller dengan nama OrderController:


php artisan make:controller OrderController

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Models\Customer;
use App\Models\Produk;
use App\Models\Order;
use App\Models\OrderItem;

class OrderController extends Controller


{
public function addToCart($id)
{
$customer = Customer::where('user_id', Auth::id())->first();
$produk = Produk::findOrFail($id);

$order = Order::firstOrCreate(
['customer_id' => $customer->id, 'status' => 'pending'],
['total_harga' => 0]
);

$orderItem = OrderItem::firstOrCreate(
['order_id' => $order->id, 'produk_id' => $produk->id],
['quantity' => 1, 'harga' => $produk->harga]
);

if (!$orderItem->wasRecentlyCreated) {
$orderItem->quantity++;
$orderItem->save();
}

$order->total_harga += $produk->harga;
$order->save();

return redirect()->route('order.cart')->with('success', 'Produk berhasil


ditambahkan ke keranjang');
}

public function viewCart()


{
$customer = Customer::where('user_id', Auth::id())->first();
$order = Order::where('customer_id', $customer->id)->where('status', 'pending',
'paid')->first();
if ($order) {
$order->load('orderItems.produk');
}
return view('v_order.cart', compact('order'));
}
}

WEB PROGRAMMING III 62


4. Tambahkan script Pada routes\web.php untuk menterjemahkan function addToCart dan
viewCart pada OrderController:
// Group route untuk customer
Route::middleware('is.customer')->group(function () {
// Route untuk menampilkan halaman akun customer
Route::get('/customer/akun/{id}', [CustomerController::class, 'akun'])
->name('customer.akun');

// Route untuk mengupdate data akun customer


Route::put('/customer/updateakun/{id}', [CustomerController::class, 'updateAkun'])
->name('customer.updateakun');

// Route untuk menambahkan produk ke keranjang


Route::post('add-to-cart/{id}', [OrderController::class, 'addToCart'])-
>name('order.addToCart');
Route::get('cart', [OrderController::class, 'viewCart'])->name('order.cart');
});

5. Pada function viewCart kita diminta untuk membuat file cart.blade.php dengan direktori
resources/views/v_order/cart.blade.php
@extends('v_layouts.app')
@section('content')
<!-- template -->

<div class="col-md-12">
<div class="order-summary clearfix">
<div class="section-title">
<p>KERANJANG</p>
<h3 class="title">Keranjang Belanja</h3>
</div>
<!-- msgSuccess -->
@if(session()->has('success'))
<div class="alert alert-success alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-
label="Close"><span aria-hidden="true">&times;</span></button>
<strong>{{ session('success') }}</strong>
</div>
@endif
<!-- end msgSuccess -->
<!-- msgError -->
@if(session()->has('error'))
<div class="alert alert-danger alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-
label="Close"><span aria-hidden="true">&times;</span></button>
<strong>{{ session('error') }}</strong>
</div>
@endif
<!-- end msgError -->
@if($order && $order->orderItems->count() > 0)
<table class="shopping-cart-table table">
<thead>
<tr>
<th>Produk</th>
<th></th>
<th class="text-center">Harga</th>
<th class="text-center">Quantity</th>
<th class="text-center">Total</th>
<th class="text-right"></th>
</tr>
</thead>
<tbody>
@php
$totalHarga = 0;
$totalBerat = 0;

WEB PROGRAMMING III 63


@endphp
@foreach($order->orderItems as $item)
@php
$totalHarga += $item->harga * $item->quantity;
$totalBerat += $item->produk->berat * $item->quantity;
@endphp
<tr>
<td class="thumb"><img src="{{ asset('storage/img-produk/thumb_sm_' .
$item->produk->foto) }}" alt=""></td>
<td class="details">
<a>{{ $item->produk->nama_produk }}</a>
<ul>
<li><span>Berat: {{ $item->produk->berat }} Gram</span></li>
</ul>
<ul>
<li><span>Stok: {{ $item->produk->stok }} Gram</span></li>
</ul>
</td>
<td class="price text-center"><strong>Rp. {{ number_format($item-
>harga, 0, ',', '.') }}</strong></td>
<td class="qty text-center">
<form action="#" method="post">
@csrf
<input type="number" name="quantity" value="{{ $item->quantity
}}" min="1" style="width: 60px;">
<button type="submit" class="btn btn-sm btn-
warning">Update</button>
</form>
</td>
<td class="total text-center"><strong class="primary-color">Rp. {{
number_format($item->harga * $item->quantity, 0, ',', '.') }}</strong></td>
<td class="text-right">
<form action="#" method="post">
@csrf
<button class="main-btn icon-btn"><i class="fa fa-
close"></i></button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>

<form action="#" method="post">


@csrf
<input type="hidden" name="total_price" value="{{ $totalHarga }}">
<input type="hidden" name="total_weight" value="{{ $totalBerat }}">
<div class="pull-right">
<button class="primary-btn">Pilih Pengiriman</button>
</div>
</form>
@else
<p>Keranjang belanja kosong.</p>
@endif
</div>
</div>

<!-- end template-->


@endsection

6. Kemudian, tambahkan tautan pada tampilan produk di semua aksi "Pesan" agar pengguna
dapat melakukan pemesanan. Misalnya pada halaman beranda pada direktori
resources/views/v_beranda/index.blade.php

WEB PROGRAMMING III 64


<form action="{{ route('order.addToCart', $row->id) }}" method="post"
style="display: inline-block;" title="Pesan Ke Aplikasi">
@csrf
<button type="submit" class="primary-btn add-to-cart"><i
class="fa fa-shopping-cart"></i> Pesan</button>
</form>

7. Saat customer melakukan pemesanan namun belum login, maka akan diarahkan ke halaman
login dengan opsi untuk masuk menggunakan akun Gmail. Namun, jika kondisi sudah login
saat klik pesan maka akan masuk ke keranjang belanja

WEB PROGRAMMING III 65


Minggu Ke-7
Integrasi API RajaOngkir untuk Ongkos Kirim

Pada pertemuan ini, kita akan membahas topik yang sangat menarik dan penting, terutama bagi
kalian yang berkecimpung dalam pengembangan aplikasi e-commerce atau sistem yang
membutuhkan informasi ongkos kirim. Kita akan mempelajari Integrasi API RajaOngkir untuk
Menghitung Ongkos Kirim. Situs resmi RajaOngkir di https://rajaongkir.com.

RajaOngkir adalah salah satu layanan populer di Indonesia yang menyediakan data ongkos
kirim dari berbagai ekspedisi pengiriman, seperti JNE, TIKI, POS Indonesia, dan masih banyak
lagi lebih detail dapat dilihat pada tautan https://rajaongkir.com/dokumentasi. Dengan
mengintegrasikan API RajaOngkir, kita bisa mendapatkan informasi ongkir secara real-time,
mulai dari tarif pengiriman, estimasi waktu pengiriman, hingga tracking resi.

Mengapa ini penting?


Karena dengan integrasi ini, kita bisa memberikan pengalaman yang lebih baik kepada
pengguna aplikasi kita. Mereka tidak perlu lagi mencari informasi ongkos kirim secara manual.
Semua informasi bisa didapatkan langsung melalui aplikasi yang kita bangun.

WEB PROGRAMMING III 66


1. Untuk melakukkan konfigurasi Environment, kita memerlukan (your_api_key) ini kita
dapatkan setelah kita melakukan register melalui https://collaborator.komerce.id/registration

RAJAONGKIR_API_KEY=your_api_key
RAJAONGKIR_BASE_URL=https://api.rajaongkir.com/starter

2. Secara default, Laravel sudah memiliki dependency yang dibutuhkan untuk melakukan
HTTP request, yaitu Laravel HTTP Client (berbasis Guzzle). Namun, jika ada masalah
dengan Http::get(), maka kita bisa memastikan Guzzle HTTP Client sudah terinstal dengan
menjalankan perintah berikut:
composer require guzzlehttp/guzzle

WEB PROGRAMMING III 67


3. Kita juga dapat memastikan apakah API Key yang digunakan berfungsi dengan baik
(pastikan dalam keadaan online). Jika berhasil, data province atau city akan ditampilkan
use Illuminate\Support\Facades\Http;

Route::get('/rajaongkir_list1', function () {
$response = Http::withHeaders([
'key' => '794a5d197b9cb469ae958ed043ccf921'
])->get('https://api.rajaongkir.com/starter/province'); //bisa ganti dengan 'province'
atau 'city'
// $statusCode = $response->json()['rajaongkir']['status']['code'];
// $provisi = $response->json()['rajaongkir']['results'];
dd($response->json());
});

Route::get('/rajaongkir_list2', function () {
$response = Http::withHeaders([
'key' => '794a5d197b9cb469ae958ed043ccf921'
])->get('https://api.rajaongkir.com/starter/province'); //bisa ganti dengan 'province'
atau 'city'
return $response->json();
});

Ketikkan pada browser http://localhost:8000/rajaongkir_list1 maka terdapat query, status &


results. Dimana jika kita klik results akan menampilkan data provinsi misalnya pada array 0
yakni dengan provinsi Bali dan begitu seterusnya

Jika menggunakan http://localhost:8000/rajaongkir_list2 sebagai berikut:

WEB PROGRAMMING III 68


Namun jika tidak berhasil bisa karena API Key tidak sesuai atau koneksi atau bisa juga terdapat
kendala lainnya, maka akan tampil sebagai berikut:

4. Setelah API Key dapat berjalan dengan baik, kita akan menjalankan aplikasi menggunakan
form sederhana tanpa template. Buatlah controller dengan nama RajaOngkirController.php.
php artisan make:controller RajaOngkirController

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;

class RajaOngkirController extends Controller


{
public function getProvinces()
{
$response = Http::withHeaders([
'key' => env('RAJAONGKIR_API_KEY')
])->get(env('RAJAONGKIR_BASE_URL') . '/province');

return response()->json($response->json());
}

public function getCities(Request $request)


{
$provinceId = $request->input('province_id');
$response = Http::withHeaders([
'key' => env('RAJAONGKIR_API_KEY')
])->get(env('RAJAONGKIR_BASE_URL') . '/city', [
'province' => $provinceId
]);

return response()->json($response->json());
}

public function getCost(Request $request)


{
$origin = $request->input('origin');
$destination = $request->input('destination');
$weight = $request->input('weight');
$courier = $request->input('courier');

$response = Http::withHeaders([
'key' => env('RAJAONGKIR_API_KEY')
])->post(env('RAJAONGKIR_BASE_URL') . '/cost', [
'origin' => $origin,
'destination' => $destination,
'weight' => $weight,
'courier' => $courier,
]);

WEB PROGRAMMING III 69


return response()->json($response->json());
}
}

5. Tambahkan script Pada routes\web.php


Route::get('/cek-ongkir', function () {
return view('ongkir');
});

Route::get('/provinces', [RajaOngkirController::class, 'getProvinces']);


Route::get('/cities', [RajaOngkirController::class, 'getCities']);
Route::post('/cost', [RajaOngkirController::class, 'getCost']);

6.
# API RajaOngkir
RAJAONGKIR_API_KEY=794a5d197b9cb469ae958ed043ccf921
RAJAONGKIR_BASE_URL=https://api.rajaongkir.com/starter

Berikutnya view pada direktori resources/views/ongkir.blade.php, untuk dapat mengganti


kode asal dapat diatur pada baris kode berikut:
let origin = 501; // Gnati Kode kota asal

dan berikut kode lengkap nya pada ongkir.blade.php


<!DOCTYPE html>
<html>

<head>
<title>Cek Ongkir</title>
<meta name="csrf-token" content="{{ csrf_token() }}">
</head>

<body>
<form id="ongkirForm">
<select name="province" id="province">
<option value="">Pilih Provinsi</option>
</select>
<select name="city" id="city">
<option value="">Pilih Kota</option>
</select>
<input type="number" name="weight" id="weight" placeholder="Berat (gram)">
<select name="courier" id="courier">
<option value="">Pilih Kurir</option>
<option value="jne">JNE</option>
<option value="tiki">TIKI</option>
<option value="pos">POS Indonesia</option>
</select>
<button type="submit">Cek Ongkir</button>
</form>

<div id="result"></div>

<script>
document.addEventListener('DOMContentLoaded', function() {
fetch('/provinces')
.then(response => response.json())
.then(data => {
console.log('Provinces data:', data);
if (data.rajaongkir.status.code === 200) {
let provinces = data.rajaongkir.results;
let provinceSelect = document.getElementById('province');
provinces.forEach(province => {
let option = document.createElement('option');

WEB PROGRAMMING III 70


option.value = province.province_id;
option.textContent = province.province;
provinceSelect.appendChild(option);
});
} else {
console.error('Failed to fetch provinces',
data.rajaongkir.status.description);
}
})
.catch(error => {
console.error('Error fetching provinces:', error);
});

document.getElementById('province').addEventListener('change', function() {
let provinceId = this.value;
fetch(`/cities?province_id=${provinceId}`)
.then(response => response.json())
.then(data => {
console.log('Cities data:', data);
if (data.rajaongkir.status.code === 200) {
let cities = data.rajaongkir.results;
let citySelect = document.getElementById('city');
citySelect.innerHTML = '';
cities.forEach(city => {
let option = document.createElement('option');
option.value = city.city_id;
option.textContent = city.city_name;
citySelect.appendChild(option);
});
} else {
console.error('Failed to fetch cities',
data.rajaongkir.status.description);
}
})
.catch(error => {
console.error('Error fetching cities:', error);
});
});

document.getElementById('ongkirForm').addEventListener('submit',
function(event) {
event.preventDefault();
let origin = 501; // Gnati Kode kota asal
let destination = document.getElementById('city').value;
let weight = document.getElementById('weight').value;
let courier = document.getElementById('courier').value;

fetch('/cost', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-
token"]').getAttribute('content')
},
body: JSON.stringify({
origin: origin,
destination: destination,
weight: weight,
courier: courier
})
})
.then(response => response.json())
.then(data => {
console.log('Cost data:', data);
if (data.rajaongkir.status.code === 200) {
let result = data.rajaongkir.results[0].costs;

WEB PROGRAMMING III 71


let resultDiv = document.getElementById('result');
resultDiv.innerHTML = '';
result.forEach(cost => {
let div = document.createElement('div');
div.textContent = `${cost.service} : ${cost.cost[0].value}
Rupiah (${cost.cost[0].etd} hari)`;
resultDiv.appendChild(div);
});
} else {
console.error('Failed to fetch cost',
data.rajaongkir.status.description);
}
})
.catch(error => {
console.error('Error fetching cost:', error);
});
});
});
</script>
</body>

</html>

Ketikkan pada browser http://localhost:8000/cek-ongkir maka kita dapat memilih Provinsi,


kemudian Pilih Kota, Isi berat paket dengan satuan gram, pilih kurir dan terakhir kita klik
Cek Ongkir maka akan ditampilkan informasi mulai dari expedisi, harga sampai dengan
estimasi waktu yang diberikan

WEB PROGRAMMING III 72


Minggu Ke-9
Proses Pemesanan dan Checkout Produk

Pada pertemuan ini, kita akan menyempurnakan materi dari pertemuan sebelumnya, yaitu
keranjang belanja (pertemuan 6) dan penghitungan ongkir (pertemuan 7). Dengan demikian,
kita dapat memproses pesanan dengan lebih lengkap, termasuk menambah dan menghapus
produk yang akan dipesan, memilih jasa pengiriman sesuai keinginan, serta melihat riwayat
pesanan yang telah dilakukan.

Pembahasan ini akan membantu kita memahami alur pemesanan dan checkout produk,
sehingga sistem yang dibangun dapat berfungsi dengan baik dalam mengelola transaksi
pelanggan.

1. Buka kembali dan kita update script pada file cart.blade.php dengan direktori
resources/views/v_order/cart.blade.php:
@extends('v_layouts.app')
@section('content')
<!-- template -->

<div class="col-md-12">
<div class="order-summary clearfix">
<div class="section-title">
<p>KERANJANG</p>
<h3 class="title">Keranjang Belanja</h3>
</div>
<!-- msgSuccess -->
@if(session()->has('success'))
<div class="alert alert-success alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-
label="Close"><span aria-hidden="true">&times;</span></button>
<strong>{{ session('success') }}</strong>
</div>
@endif
<!-- end msgSuccess -->
<!-- msgError -->
@if(session()->has('error'))
<div class="alert alert-danger alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-
label="Close"><span aria-hidden="true">&times;</span></button>
<strong>{{ session('error') }}</strong>
</div>
@endif
<!-- end msgError -->
@if($order && $order->orderItems->count() > 0)
<table class="shopping-cart-table table">
<thead>
<tr>
<th>Produk</th>
<th></th>
<th class="text-center">Harga</th>
<th class="text-center">Quantity</th>
<th class="text-center">Total</th>
<th class="text-right"></th>
</tr>
</thead>
<tbody>
@php
$totalHarga = 0;
$totalBerat = 0;
@endphp

WEB PROGRAMMING III 73


@foreach($order->orderItems as $item)
@php
$totalHarga += $item->harga * $item->quantity;
$totalBerat += $item->produk->berat * $item->quantity;
@endphp
<tr>
<td class="thumb"><img src="{{ asset('storage/img-produk/thumb_sm_' .
$item->produk->foto) }}" alt=""></td>
<td class="details">
<a>{{ $item->produk->nama_produk }}</a>
<ul>
<li><span>Berat: {{ $item->produk->berat }} Gram</span></li>
</ul>
<ul>
<li><span>Stok: {{ $item->produk->stok }} Gram</span></li>
</ul>
</td>
<td class="price text-center"><strong>Rp. {{ number_format($item-
>harga, 0, ',', '.') }}</strong></td>
<td class="qty text-center">
<form action="{{ route('order.updateCart', $item->id) }}"
method="post">
@csrf
<input type="number" name="quantity" value="{{ $item->quantity
}}" min="1" style="width: 60px;">
<button type="submit" class="btn btn-sm btn-
warning">Update</button>
</form>
</td>
<td class="total text-center"><strong class="primary-color">Rp. {{
number_format($item->harga * $item->quantity, 0, ',', '.') }}</strong></td>
<td class="text-right">
<form action="{{ route('order.remove', $item->produk->id) }}"
method="post">
@csrf
<button class="main-btn icon-btn"><i class="fa fa-
close"></i></button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>

<form action="#" method="post">


@csrf
<input type="hidden" name="total_price" value="{{ $totalHarga }}">
<input type="hidden" name="total_weight" value="{{ $totalBerat }}">
<div class="pull-right">
<button class="primary-btn">Pilih Pengiriman</button>
</div>
</form>
@else
<p>Keranjang belanja kosong.</p>
@endif
</div>
</div>

<!-- end template-->


@endsection

WEB PROGRAMMING III 74


2. sehingga kita perlu menambahkan script pada routes\web.php, berikut lengkapnya pada
group route untuk is.customer sebagai berikut:
// Group route untuk customer
Route::middleware('is.customer')->group(function () {
// Route untuk menampilkan halaman akun customer
Route::post('cart/update/{id}', [OrderController::class, 'updateCart'])-
>name('order.updateCart');
Route::post('remove/{id}', [OrderController::class, 'removeFromCart'])-
>name('order.remove');

});

Berikut lengkap lengkapnya dari is.customer sebagai berikut:


// Group route untuk customer
Route::middleware('is.customer')->group(function () {
// Route untuk menampilkan halaman akun customer
Route::get('/customer/akun/{id}', [CustomerController::class, 'akun'])
->name('customer.akun');

// Route untuk mengupdate data akun customer


Route::put('/customer/updateakun/{id}', [CustomerController::class, 'updateAkun'])
->name('customer.updateakun');

// Route untuk menambahkan produk ke keranjang


Route::post('add-to-cart/{id}', [OrderController::class, 'addToCart'])-
>name('order.addToCart');
Route::get('cart', [OrderController::class, 'viewCart'])->name('order.cart');
Route::post('cart/update/{id}', [OrderController::class, 'updateCart'])-
>name('order.updateCart');
Route::post('remove/{id}', [OrderController::class, 'removeFromCart'])-
>name('order.remove');

});

3. sehingga kita perlu menambahkan function updateCart dan removeFromCart dari pada
controller yakni OrderController, dan berikut script lengkap pada OrderController:
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Models\Customer;
use App\Models\Produk;
use App\Models\Order;
use App\Models\OrderItem;

class OrderController extends Controller


{
public function addToCart($id)
{
$customer = Customer::where('user_id', Auth::id())->first();
$produk = Produk::findOrFail($id);

$order = Order::firstOrCreate(
['customer_id' => $customer->id, 'status' => 'pending'],
['total_harga' => 0]
);

$orderItem = OrderItem::firstOrCreate(
['order_id' => $order->id, 'produk_id' => $produk->id],
['quantity' => 1, 'harga' => $produk->harga]
);

WEB PROGRAMMING III 75


if (!$orderItem->wasRecentlyCreated) {
$orderItem->quantity++;
$orderItem->save();
}

$order->total_harga += $produk->harga;
$order->save();

return redirect()->route('order.cart')->with('success', 'Produk berhasil


ditambahkan ke keranjang');
}

public function viewCart()


{
$customer = Customer::where('user_id', Auth::id())->first();
$order = Order::where('customer_id', $customer->id)->where('status', 'pending')-
>first();
// Pastikan $order ada
if (!$order) {
return redirect()->route('order.cart')->with('error', 'Order tidak
ditemukan.');
}
// Load relasi orderItems
$order->load('orderItems.produk');

return view('v_order.cart', compact('order'));


}

public function updateCart(Request $request, $id)


{
$customer = Customer::where('user_id', Auth::id())->first();;;
$order = Order::where('customer_id', $customer->id)->where('status', 'pending')-
>first();
if ($order) {
$orderItem = $order->orderItems()->where('id', $id)->first();
if ($orderItem) {
$quantity = $request->input('quantity');
if ($quantity > $orderItem->produk->stok) {
return redirect()->route('order.cart')->with('error', 'Jumlah produk
melebihi stok yang tersedia');
}
$order->total_harga -= $orderItem->harga * $orderItem->quantity;
$orderItem->quantity = $quantity;
$orderItem->save();
$order->total_harga += $orderItem->harga * $orderItem->quantity;
$order->save();
}
}
return redirect()->route('order.cart')->with('success', 'Jumlah produk berhasil
diperbarui');
}

public function removeFromCart(Request $request, $id)


{
$customer = Customer::where('user_id', Auth::id())->first();
$order = Order::where('customer_id', $customer->id)->where('status', 'pending')-
>first();

if ($order) {
$orderItem = OrderItem::where('order_id', $order->id)->where('produk_id', $id)-
>first();

if ($orderItem) {
$order->total_harga -= $orderItem->harga * $orderItem->quantity;
$orderItem->delete();

WEB PROGRAMMING III 76


if ($order->total_harga <= 0) {
$order->delete();
} else {
$order->save();
}
}
}
return redirect()->route('order.cart')->with('success', 'Produk berhasil dihapus
dari keranjang');
}
}

Klik tombol Update untuk memperbarui Total, sedangkan tombol x digunakan untuk
menghapus data, jika kita cek pada database data sudah masuk di tabel order dan order_item

WEB PROGRAMMING III 77


4. Sebelum masuk ke aksi PILIH KIRIM kita update script pada file cart.blade.php dengan
direktori resources/views/v_order/cart.blade.php yakni pada form action sebagai berikut:
<form action="#" method="post">
@csrf
<input type="hidden" name="total_price" value="{{ $totalHarga }}">
<input type="hidden" name="total_weight" value="{{ $totalBerat }}">
<div class="pull-right">
<button class="primary-btn">Pilih Pengiriman</button>
</div>
</form>

Menjadi
<form action="{{ route('order.select-shipping') }}" method="post">
@csrf
<input type="hidden" name="total_price" value="{{ $totalHarga }}">
<input type="hidden" name="total_weight" value="{{ $totalBerat }}">
<div class="pull-right">
<button class="primary-btn">Pilih Pengiriman</button>
</div>
</form>

5. Sehingga kita update kembali routes\web.php, pada group route untuk is.customer:
Route::post('select-shipping', [OrderController::class, 'selectShipping'])-
>name('order.select-shipping');
Route::post('update-ongkir', [OrderController::class, 'updateOngkir'])-
>name('order.update-ongkir');
Route::get('select-payment', [OrderController::class, 'selectPayment'])-
>name('order.selectpayment');

berikut lengkapnya pada group route untuk is.customer sebagai berikut:


// Group route untuk customer
Route::middleware('is.customer')->group(function () {
// Route untuk menampilkan halaman akun customer
Route::get('/customer/akun/{id}', [CustomerController::class, 'akun'])
->name('customer.akun');

// Route untuk mengupdate data akun customer


Route::put('/customer/updateakun/{id}', [CustomerController::class, 'updateAkun'])
->name('customer.updateakun');

// Route untuk menambahkan produk ke keranjang


Route::post('add-to-cart/{id}', [OrderController::class, 'addToCart'])-
>name('order.addToCart');
Route::get('cart', [OrderController::class, 'viewCart'])->name('order.cart');
Route::post('cart/update/{id}', [OrderController::class, 'updateCart'])-
>name('order.updateCart');
Route::post('remove/{id}', [OrderController::class, 'removeFromCart'])-
>name('order.remove');
Route::post('select-shipping', [OrderController::class, 'selectShipping'])-
>name('order.select-shipping');
Route::post('update-ongkir', [OrderController::class, 'updateOngkir'])-
>name('order.update-ongkir');
Route::get('select-payment', [OrderController::class, 'selectPayment'])-
>name('order.selectpayment');
});

WEB PROGRAMMING III 78


6. sehingga kita perlu menambahkan function selectShipping, updateOngkir dan
selectPayment dari pada controller yakni OrderController, dan berikut script lengkap pada
OrderController:
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Models\Customer;
use App\Models\Produk;
use App\Models\Order;
use App\Models\OrderItem;

class OrderController extends Controller


{
public function addToCart($id)
{
$customer = Customer::where('user_id', Auth::id())->first();
$produk = Produk::findOrFail($id);

$order = Order::firstOrCreate(
['customer_id' => $customer->id, 'status' => 'pending'],
['total_harga' => 0]
);

$orderItem = OrderItem::firstOrCreate(
['order_id' => $order->id, 'produk_id' => $produk->id],
['quantity' => 1, 'harga' => $produk->harga]
);

if (!$orderItem->wasRecentlyCreated) {
$orderItem->quantity++;
$orderItem->save();
}

$order->total_harga += $produk->harga;
$order->save();

return redirect()->route('order.cart')->with('success', 'Produk berhasil


ditambahkan ke keranjang');
}

public function viewCart()


{
$customer = Customer::where('user_id', Auth::id())->first();
$order = Order::where('customer_id', $customer->id)->where('status', 'pending')-
>first();
// Pastikan $order ada
if (!$order) {
return redirect()->route('order.cart')->with('error', 'Order tidak
ditemukan.');
}
// Load relasi orderItems
$order->load('orderItems.produk');

return view('v_order.cart', compact('order'));


}

public function updateCart(Request $request, $id)


{
$customer = Customer::where('user_id', Auth::id())->first();;;
$order = Order::where('customer_id', $customer->id)->where('status', 'pending')-
>first();
if ($order) {

WEB PROGRAMMING III 79


$orderItem = $order->orderItems()->where('id', $id)->first();
if ($orderItem) {
$quantity = $request->input('quantity');
if ($quantity > $orderItem->produk->stok) {
return redirect()->route('order.cart')->with('error', 'Jumlah produk
melebihi stok yang tersedia');
}
$order->total_harga -= $orderItem->harga * $orderItem->quantity;
$orderItem->quantity = $quantity;
$orderItem->save();
$order->total_harga += $orderItem->harga * $orderItem->quantity;
$order->save();
}
}
return redirect()->route('order.cart')->with('success', 'Jumlah produk berhasil
diperbarui');
}

public function removeFromCart(Request $request, $id)


{
$customer = Customer::where('user_id', Auth::id())->first();
$order = Order::where('customer_id', $customer->id)->where('status', 'pending')-
>first();

if ($order) {
$orderItem = OrderItem::where('order_id', $order->id)->where('produk_id', $id)-
>first();

if ($orderItem) {
$order->total_harga -= $orderItem->harga * $orderItem->quantity;
$orderItem->delete();

if ($order->total_harga <= 0) {
$order->delete();
} else {
$order->save();
}
}
}
return redirect()->route('order.cart')->with('success', 'Produk berhasil dihapus
dari keranjang');
}

public function selectShipping(Request $request)


{
// Mendapatkan customer berdasarkan user yang login
$customer = Customer::where('user_id', Auth::id())->first();

// Pastikan order dengan status 'pending' ada untuk customer ini


$order = Order::where('customer_id', $customer->id)->where('status', 'pending')-
>first();

// Cek apakah order ada


if (!$order) {
return redirect()->route('order.cart')->with('error', 'Keranjang belanja
kosong.');
}

// Pastikan orderItems sudah dimuat menggunakan eager loading


$order->load('orderItems.produk');

// Lanjutkan ke view jika order ada


return view('v_order.select_shipping', compact('order'));
}

public function updateOngkir(Request $request)

WEB PROGRAMMING III 80


{
$customer = Customer::where('user_id', Auth::id())->first();
$order = Order::where('customer_id', $customer->id)->where('status', 'pending')-
>first();

$origin = $request->input('city_origin'); // kode kota asal


$originName = $request->input('city_origin_name'); // nama kota asal

if ($order) {
// Simpan data ongkir ke dalam order
$order->kurir = $request->input('kurir');
$order->layanan_ongkir = $request->input('layanan_ongkir');
$order->biaya_ongkir = $request->input('biaya_ongkir');
$order->estimasi_ongkir = $request->input('estimasi_ongkir');
$order->total_berat = $request->input('total_berat');
$order->alamat = $request->input('alamat') . ', <br>' . $request-
>input('city_name') . ', <br>' . $request->input('province_name');
$order->pos = $request->input('pos');
$order->save();
// Simpan ke session flash agar bisa diakses di halaman tujuan
return redirect()->route('order.selectpayment')
->with('origin', $origin)
->with('originName', $originName);
}

return back()->with('error', 'Gagal menyimpan data ongkir');


}

public function selectPayment()


{
// Mendapatkan customer yang login
$customer = Auth::user();

// Cari order dengan status 'pending'


$order = Order::where('customer_id', $customer->customer->id)->where('status',
'pending')->first();

$origin = session('origin'); // Kode kota asal


$originName = session('originName'); // Nama kota asal

// Jika order tidak ditemukan, tampilkan error


if (!$order) {
return redirect()->route('order.cart')->with('error', 'Keranjang belanja
kosong.');
}

// Muat relasi orderItems dan produk terkait


$order->load('orderItems.produk');

// Hitung total harga produk


$totalHarga = 0;
foreach ($order->orderItems as $item) {
$totalHarga += $item->harga * $item->quantity;
}

// Kirim data order dan snapToken ke view


return view('v_order.select_payment', [
'order' => $order,
'origin' => $origin,
'originName' => $originName,
// 'snapToken' => $snapToken
]);
}
}

WEB PROGRAMMING III 81


7. Pada app.balde.php yakni pada frontend sisipkan token CSRF (Cross-Site Request
Forgery) ke dalam halaman web agar dapat digunakan oleh JavaScript pada klien (browser)
untuk mengamankan permintaan HTTP:
<meta name="csrf-token" content="{{ csrf_token() }}">

✓ CSRF Token adalah sebuah string unik yang dibuat oleh server untuk melindungi aplikasi web dari
serangan CSRF.
✓ Token ini harus dikirim bersama setiap permintaan yang memodifikasi data di server supaya server
bisa memverifikasi bahwa permintaan tersebut benar-benar berasal dari pengguna yang sah dan
bukan dari pihak luar yang mencoba menyerang.
✓ Dengan menempatkan token ini di dalam elemen <meta>, JavaScript pada halaman bisa membaca
token ini dan menyertakannya dalam header HTTP (misalnya X-CSRF-TOKEN) pada setiap
permintaan AJAX.
✓ Sintaks {{ csrf_token() }} biasanya digunakan di framework seperti Laravel untuk menampilkan
token CSRF yang di-generate server ke halaman HTML.

8. Untuk view dari function selectShipping maka kita tambahkan pada file
select_shipping.blade.php didirektori resources/views/v_order/select_shipping.blade.php
sebagai berikut:
@extends('v_layouts.app')
@section('content')
<!-- template -->

<div class="col-md-12" hidden>


<div class="order-summary clearfix">
<div class="section-title">
<p>PENGIRIMAN</p>
<h3 class="title">Produk</h3>
</div>
@if($order && $order->orderItems->count() > 0)
<table class="shopping-cart-table table">
<thead>
<tr>
<th>Produk</th>
<th></th>
<th class="text-center">Harga</th>
<th class="text-center">Quantity</th>
<th class="text-center">Total</th>
</tr>
</thead>
<tbody>

WEB PROGRAMMING III 82


@php
$totalHarga = 0;
$totalBerat = 0;
@endphp
@foreach($order->orderItems as $item)
@php
$totalHarga += $item->harga * $item->quantity;
$totalBerat += $item->produk->berat * $item->quantity;
@endphp
<tr>
<td class="thumb"><img src="{{ asset('storage/img-produk/thumb_sm_' .
$item->produk->foto) }}" alt=""></td>
<td class="details">
<a>{{ $item->produk->nama_produk }}</a>
<ul>
<li><span>Berat: {{ $item->produk->berat }} Gram</span></li>
</ul>
<ul>
<li><span>Stok: {{ $item->produk->stok }} Gram</span></li>
</ul>
</td>
<td class="price text-center"><strong>Rp. {{ number_format($item-
>harga, 0, ',', '.') }}</strong></td>
<td class="qty text-center">
<a> {{ $item->quantity }} </a>
</td>
<td class="total text-center"><strong class="primary-color">Rp. {{
number_format($item->harga * $item->quantity, 0, ',', '.') }}</strong></td>
</tr>
@endforeach
</tbody>
</table>
@else
<p>Keranjang belanja kosong.</p>
@endif
</div>
</div>

<div class="col-md-12">
<div class="order-summary clearfix">
<div class="section-title">
<p>PENGIRIMAN</p>
<h3 class="title">Pilih Pengiriman</h3>
</div>
<form id="shippingForm">
<!-- Kota Asal -->
<input type="hidden" id="city_origin" name="city_origin" value="">
<input type="hidden" id="city_origin_name" name="city_origin_name" value="">
<!-- /Kota Asal -->

<div class="form-group">
<label for="province">Provinsi Tujuan:</label>
<select name="province" id="province" class="input">
<option value="">Pilih Provinsi Tujuan</option>
<!-- Data Provinsi Tujuan akan dimuat dengan JavaScript -->
</select>
</div>
<div class="form-group">
<label for="city">Kota Tujuan:</label>
<select name="city" id="city" class="input">
<option value="">Pilih Kota Tujuan</option>
<!-- Data Kota Tujuan akan dimuat dengan JavaScript -->
</select>
</div>
<input type="hidden" name="weight" id="weight" value="{{ $totalBerat }}">
<input type="hidden" name="province_name" id="province_name">

WEB PROGRAMMING III 83


<input type="hidden" name="city_name" id="city_name">
<div class="form-group">
<label for="courier">Kurir:</label>
<select name="courier" id="courier" class="input">
<option value="">Pilih Kurir</option>
<option value="jne">JNE</option>
<option value="tiki">TIKI</option>
<option value="pos">POS Indonesia</option>
</select>
</div>
<div class="form-group">
<label for="">Alamat</label>
<textarea class="input" name="alamat" id="alamat">{{ Auth::user()->alamat
}}</textarea>
</div>
<div class="form-group">
<label for="">Kode Pos</label>
<input type="text" class="input" name="kode_pos" id="kode_pos" value="{{
Auth::user()->pos }}">
</div>
<button type="submit" class="primary-btn">Cek Ongkir</button>
</form>

<br>
<div id="result">
<table class="shopping-cart-table table">
<thead>
<tr>
<th>Layanan</th>
<th>Biaya</th>
<th>Estimasi Pengiriman</th>
<th>Total Berat</th>
<th>Total Harga</th>
<th>Bayar</th>
</tr>
</thead>
<tbody id="shippingResults">
<!-- Hasil dari pencarian akan dimuat di sini -->
</tbody>
</table>
</div>
</div>
</div>

<script>
document.addEventListener('DOMContentLoaded', function() {
const originCityCode = 115; //ganti disini untuk kode kota asal
const originCityName = 'Depok'; //ganti disini untuk nama kota asal

document.getElementById('city_origin').value = originCityCode;
document.getElementById('city_origin_name').value = originCityName;

// Load provinces
fetch('/provinces')
.then(response => response.json())
.then(data => {
if (data.rajaongkir.status.code === 200) {
let provinces = data.rajaongkir.results;
let provinceSelect = document.getElementById('province');
provinces.forEach(province => {
let option = document.createElement('option');
option.value = province.province_id;
option.textContent = province.province;
provinceSelect.appendChild(option);

WEB PROGRAMMING III 84


});
} else {
console.error('Failed to fetch provinces',
data.rajaongkir.status.description);
}
})
.catch(error => {
console.error('Error fetching provinces:', error);
});

// Load cities based on selected province


document.getElementById('province').addEventListener('change', function() {
let provinceId = this.value;
let provinceName = this.options[this.selectedIndex].text;
document.getElementById('province_name').value = provinceName;

fetch(`/cities?province_id=${provinceId}`)
.then(response => response.json())
.then(data => {
if (data.rajaongkir.status.code === 200) {
let cities = data.rajaongkir.results;
let citySelect = document.getElementById('city');
citySelect.innerHTML = '<option value="">Pilih Kota
Tujuan</option>'; // Clear previous options
cities.forEach(city => {
let option = document.createElement('option');
option.value = city.city_id;
option.textContent = city.city_name;
citySelect.appendChild(option);
});
} else {
console.error('Failed to fetch cities',
data.rajaongkir.status.description);
}
})
.catch(error => {
console.error('Error fetching cities:', error);
});
});

document.getElementById('city').addEventListener('change', function() {
let cityName = this.options[this.selectedIndex].text;
document.getElementById('city_name').value = cityName;
});

// Handle form submission for shipping cost check


document.getElementById('shippingForm').addEventListener('submit', function(event)
{
event.preventDefault();
let origin = document.getElementById('city_origin').value;
let originName = document.getElementById('city_origin_name').value;
let destination = document.getElementById('city').value;
let weight = document.getElementById('weight').value;
let courier = document.getElementById('courier').value;
let alamat = document.getElementById('alamat').value;
let kodePos = document.getElementById('kode_pos').value;

// Validasi alamat dan kode pos


if (!alamat.trim() || !kodePos.trim()) {
alert('Harap lengkapi alamat dan kode pos sebelum mengecek ongkir.');
return;
}

if (!origin || !originName || !destination || !weight || !courier) {


alert('Harap lengkapi semua kolom sebelum mengecek ongkir.');
return;

WEB PROGRAMMING III 85


}

fetch('/cost', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-
token"]').getAttribute('content')
},
body: JSON.stringify({
origin: origin,
destination: destination,
weight: weight,
courier: courier
})
})
.then(response => response.json())
.then(data => {
if (data.rajaongkir.status.code === 200) {
let result = data.rajaongkir.results[0].costs;
let shippingResults = document.getElementById('shippingResults');
shippingResults.innerHTML = ''; // Clear previous results
result.forEach(cost => {
let row = document.createElement('tr');
row.innerHTML = `
<td>${cost.service}</td>
<td>${cost.cost[0].value} Rupiah</td>
<td>${cost.cost[0].etd} hari</td>
<td>${weight} Gram</td>
<td>Rp. {{ number_format($totalHarga, 0, ',', '.') }}</td>
<td>
<form action="{{ route('order.update-ongkir') }}"
method="post">
@csrf
<input type="hidden" name="province"
value="${document.getElementById('province').value}">
<input type="hidden" name="city"
value="${document.getElementById('city').value}">
<input type="hidden" name="province_name"
value="${document.getElementById('province_name').value}">
<input type="hidden" name="city_name"
value="${document.getElementById('city_name').value}">
<input type="hidden" name="kurir"
value="${courier}">
<input type="hidden" name="alamat"
value="${alamat}">
<input type="hidden" name="pos" value="${kodePos}">
<input type="hidden" name="layanan_ongkir"
value="${cost.service}">
<input type="hidden" name="biaya_ongkir"
value="${cost.cost[0].value}">
<input type="hidden" name="estimasi_ongkir"
value="${cost.cost[0].etd}">
<input type="hidden" name="total_berat"
value="${weight}">
<input type="hidden" name="city_origin"
value="${origin}">
<input type="hidden" name="city_origin_name"
value="${originName}">
<button type="submit" class="primary-btn">Pilih
Pengiriman</button>
</form>
</td>
`;
shippingResults.appendChild(row);
});

WEB PROGRAMMING III 86


} else {
console.error('Failed to fetch cost',
data.rajaongkir.status.description);
}
})
.catch(error => {
console.error('Error fetching cost:', error);
});
});
});
</script>

<!-- end template-->


@endsection

Pada baris script ini kita bisa mengganti dengan kota asal yang kita inginkan:
<script>
document.addEventListener('DOMContentLoaded', function() {
const originCityCode = 115; //ganti disini untuk kode kota asal
const originCityName = 'Depok'; //ganti disini untuk nama kota asal

document.getElementById('city_origin').value = originCityCode;
document.getElementById('city_origin_name').value = originCityName;
//script berikutnya >>
</script>

Jika berhasil, akan muncul halaman select-shipping seperti gambar di bawah ini. Isi data seperti
Provinsi Tujuan, Kota Tujuan, Kurir, Alamat, dan Kode Pos, kemudian klik tombol Cek
Ongkir untuk mendapatkan pilihan pengiriman yang diinginkan:

WEB PROGRAMMING III 87


9. Sedangkan Untuk view dari function selectPayment maka kita tambahkan pada file
select_payment.blade.php didirektori resources/views/v_order/select_payment.blade.php
sebagai berikut:
@extends('v_layouts.app')
@section('content')
<div class="col-md-12">
<div class="order-summary clearfix">
<div class="section-title">
<p>KERANJANG</p>
<h3 class="title">Keranjang Belanja</h3>
</div>
@if(session()->has('success'))
<div class="alert alert-success alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-
label="Close"><span aria-hidden="true">&times;</span></button>
<strong>{{ session('success') }}</strong>
</div>
@endif
@if(session()->has('error'))
<div class="alert alert-danger alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-
label="Close"><span aria-hidden="true">&times;</span></button>
<strong>{{ session('error') }}</strong>
</div>
@endif
@if($order && $order->orderItems->count() > 0)
<table class="shopping-cart-table table">
<thead>
<tr>
<th>Produk</th>
<th></th>
<th class="text-center">Harga</th>
<th class="text-center">Quantity</th>
<th class="text-center">Total</th>

</tr>
</thead>
<tbody>
@php
$totalHarga = 0;
$totalBerat = 0;
@endphp
@foreach($order->orderItems as $item)
@php
$totalHarga += $item->harga * $item->quantity;
$totalBerat += $item->produk->berat * $item->quantity;
@endphp
<tr>
<td class="thumb"><img src="{{ asset('storage/img-produk/thumb_sm_' .
$item->produk->foto) }}" alt=""></td>
<td class="details">
<a>{{ $item->produk->nama_produk }}</a>
<ul>
<li><span>Berat: {{ $item->produk->berat }} Gram</span></li>
</ul>
<ul>
<li><span>Stok: {{ $item->produk->stok }} Gram</span></li>
</ul>
</td>
<td class="price text-center"><strong>Rp. {{ number_format($item-
>harga, 0, ',', '.') }}</strong></td>
<td class="qty text-center">
<a> {{ $item->quantity }} </a>
</td>

WEB PROGRAMMING III 88


<td class="total text-center"><strong class="primary-color">Rp. {{
number_format($item->harga * $item->quantity, 0, ',', '.') }}</strong></td>

</tr>
@endforeach
</tbody>
<tfoot>
<tr>
<th class="empty" colspan="3"></th>
<th>SUBTOTAL</th>
<th colspan="2" class="sub-total">Rp. {{ number_format($totalHarga, 0,
',', '.') }}</th>
</tr>
<tr>
<th class="empty" colspan="3"></th>
<th>Ongkos Kirim</th>
<td colspan="2">
Rp. {{ number_format($order->biaya_ongkir, 0, ',', '.') }} <br>
{{ $order->kurir.'.'.$order->layanan_ongkir.' *estimasi '. $order-
>estimasi_ongkir.' Hari'}}
@if(session('origin'))
<p>Kota asal: {{ $originName }} </p>
@endif

</td>
</tr>
<tr>
<th class="empty" colspan="3"></th>
<th>TOTAL BAYAR</th>
<th colspan="2" class="total">Rp. {{ number_format($totalHarga +
$order->biaya_ongkir, 0, ',', '.') }}</th>
</tr>
</tfoot>
</table>

<input type="hidden" name="total_price" value="{{ $totalHarga }}">


<input type="hidden" name="total_weight" value="{{ $totalBerat }}">
<div class="pull-right">
<button class="primary-btn" id="pay-button">Bayar Sekarang</button>
</div>
@else
<p>Keranjang belanja kosong.</p>
@endif
</div>
</div>
<!--

@endsection

10. kemudian kita tambahkan relasi customer di model User sebagai berikut:
public function customer()
{
return $this->hasOne(Customer::class);
}

Dan berikut script lengkap dari model User:


<?php

namespace App\Models;

// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

WEB PROGRAMMING III 89


use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable


{
use HasApiTokens, HasFactory, Notifiable;

/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $table = "user";
protected $fillable = [
'nama',
'email',
'role',
'status',
'password',
'hp',
'foto',
];

/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];

/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'email_verified_at' => 'datetime',
];

public function customer()


{
return $this->hasOne(Customer::class);
}
}

WEB PROGRAMMING III 90


11. Kemudian pilih aksi PILIH PENGIRIM pada select-shipping maka akan tampil select-
payment sebagai berikut:

WEB PROGRAMMING III 91


Minggu Ke-10
Payment Gateway dengan Midtrans

Payment Gateway adalah sebuah layanan yang memfasilitasi proses pembayaran online dengan
menghubungkan antara merchant (penjual) dan pembeli melalui berbagai metode pembayaran.
Payment Gateway berfungsi sebagai perantara yang memastikan transaksi berlangsung aman,
cepat, dan efisien. Layanan ini sangat penting dalam e-commerce dan bisnis online karena
memungkinkan pembayaran dilakukan secara digital.

Apa itu Midtrans?

Midtrans adalah salah satu penyedia Payment Gateway terkemuka di Indonesia yang
memungkinkan bisnis untuk menerima pembayaran online dengan berbagai metode. Midtrans
didirikan pada tahun 2015 dan merupakan bagian dari Gojek Group. Layanan ini dirancang
untuk memudahkan merchant dalam mengelola transaksi pembayaran dengan fitur-fitur yang
lengkap dan integrasi yang mudah. Midtrans menawarkan berbagai fitur yang membuatnya
menjadi pilihan populer bagi banyak bisnis online, di antaranya:
1. Multi-Channel Payment: Midtrans mendukung berbagai metode pembayaran, termasuk
transfer bank, kartu kredit/debit, e-wallet (seperti GoPay, OVO, Dana, ShopeePay), dan
gerai retail (seperti Alfamart, Indomaret).
2. One-Click Checkout: Memungkinkan pelanggan untuk menyimpan informasi pembayaran
mereka sehingga transaksi berikutnya dapat dilakukan dengan satu klik.
3. Recurring Payment: Cocok untuk bisnis yang menawarkan layanan berlangganan, seperti
membership atau subscription.
4. Fraud Detection: Midtrans dilengkapi dengan sistem deteksi penipuan yang canggih untuk
melindungi merchant dan pelanggan dari transaksi yang mencurigakan.
5. Customizable Checkout Page: Merchant dapat menyesuaikan halaman pembayaran sesuai
dengan brand mereka.
6. Real-Time Reporting: Merchant dapat memantau transaksi secara real-time melalui
dashboard Midtrans.

WEB PROGRAMMING III 92


7. API dan Plugin yang Mudah Diintegrasikan: Midtrans menyediakan API dan plugin untuk
berbagai platform e-commerce seperti Shopify, WooCommerce, Magento, dan lainnya.
Proses transaksi menggunakan Midtrans dapat dijelaskan sebagai berikut:
1. Pembeli Memilih Produk: Pembeli memilih produk atau layanan yang ingin dibeli di
website atau aplikasi merchant.
2. Checkout: Pembeli diarahkan ke halaman pembayaran Midtrans untuk memilih metode
pembayaran.
3. Pembayaran: Pembeli melakukan pembayaran melalui metode yang dipilih (transfer bank,
kartu kredit, e-wallet, dll.).
4. Verifikasi Pembayaran: Midtrans memverifikasi pembayaran dan mengirimkan konfirmasi
ke merchant.
5. Transaksi Selesai: Merchant menerima pembayaran dan mengirimkan produk atau layanan
kepada pembeli.
1. Midtrans dengan url resminya yakni https://midtrans.com/ silahkan melakukan daftar dengan
url https://dashboard.midtrans.com/register kemudian lengkapi data dan pastikan email yang
dimasukkan email aktif

WEB PROGRAMMING III 93


2. Setelah Data lengkap dan klik tombol Daftra maka akan verifikasi melalui email:

Jika berhasil login maka akan ditampilkan kehalaman dashboard midtrans sebagai
berikut:

WEB PROGRAMMING III 94


3. Pada halaman dashboard Midtrans, kita ubah pengaturan dari mode production menjadi
sandbox. Mode sandbox digunakan untuk pengujian transaksi secara aman tanpa melibatkan
uang sungguhan, sedangkan mode production digunakan untuk transaksi sebenarnya.

4. Pada Pengaturan -> Access Keys disinilah kita mendapat Akses Key

WEB PROGRAMMING III 95


5. Kendala yang kita hadapi yakni URL lokal tidak dapat digunakan karena URL yang diterima
harus berasal dari proyek yang sudah menggunakan domain. Pada pengaturan URL notifikasi,
kita dapat memasukkan domain tersebut. Namun, jika menggunakan lokal, maka URL
notifikasi dapat dikosongkan. Berikut ini contoh pengisian URL notifikasi ketika sudah
memiliki domain:
https://namadomain.com/api/order/midtrans-callback

6. Demikian beberapa pengaturan yang dapat kita lakukan pada Midtrans. Selanjutnya, kita
kembali ke proyek. Pertama, kita install library Midtrans terlebih dahulu melalui terminal:
composer require midtrans/midtrans-php

WEB PROGRAMMING III 96


Tunggu hingga proses download selesai, jika sudah selesai kita dapat mengecek apakah
library Midtrans sudah ditambahkan dalam projek kita
composer show midtrans/midtrans-php

7. Kita tambahkan Access Keys Midtrans pada Env sebagai berikut:


# API Midtrans
MIDTRANS_MERCHANT_ID=G048881506
MIDTRANS_CLIENT_KEY=SB-Mid-client-vEo6Cq0Iz9cw-Qj4
MIDTRANS_SERVER_KEY=SB-Mid-server-FV5c5fN68aHkAESEerWJ74XB

Kemudian Pada route/api.php kita tambahkan sebagai berikut


<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\OrderController;

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider and all of them will

WEB PROGRAMMING III 97


| be assigned to the "api" middleware group. Make something great!
|
*/

Route::middleware('auth:sanctum')->get('/user', function (Request $request) {


return $request->user();
});
Route::post('/order/midtrans-callback', [OrderController::class, 'callback'])-
>name('order.callback');

Berikutnya yakni pada views/v_layouts/app.blade.php


<script type="text/javascript" src="https://app.sandbox.midtrans.com/snap/snap.js"
data-client-key="{{ config('midtrans.client_key') }}">
</script>

8. Tambahkan file midtrans.php pada direktori config sebagai berikut:


<?php

return [
'merchant_id' => env('MIDTRANS_MERCHANT_ID'),
'client_key' => env('MIDTRANS_CLIENT_KEY'),
'server_key' => env('MIDTRANS_SERVER_KEY'),
'is_production' => env('MIDTRANS_IS_PRODUCTION'),
'is_sanitized' => env('MIDTRANS_IS_SANITIZED'),
'is_3ds' => env('MIDTRANS_IS_3DS'),
];

WEB PROGRAMMING III 98


9. Kita update script pada file select_payment.blade.php didirektori resources/views/
v_order/select_payment.blade.php sebagai berikut:
@extends('v_layouts.app')
@section('content')
<div class="col-md-12">
<div class="order-summary clearfix">
<div class="section-title">
<p>KERANJANG</p>
<h3 class="title">Keranjang Belanja</h3>
</div>
@if(session()->has('success'))
<div class="alert alert-success alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-
label="Close"><span aria-hidden="true">&times;</span></button>
<strong>{{ session('success') }}</strong>
</div>
@endif
@if(session()->has('error'))
<div class="alert alert-danger alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-
label="Close"><span aria-hidden="true">&times;</span></button>
<strong>{{ session('error') }}</strong>
</div>
@endif
@if($order && $order->orderItems->count() > 0)
<table class="shopping-cart-table table">
<thead>
<tr>
<th>Produk</th>
<th></th>
<th class="text-center">Harga</th>
<th class="text-center">Quantity</th>
<th class="text-center">Total</th>

</tr>
</thead>
<tbody>
@php
$totalHarga = 0;
$totalBerat = 0;
@endphp
@foreach($order->orderItems as $item)
@php
$totalHarga += $item->harga * $item->quantity;
$totalBerat += $item->produk->berat * $item->quantity;
@endphp

WEB PROGRAMMING III 99


<tr>
<td class="thumb"><img src="{{ asset('storage/img-produk/thumb_sm_' .
$item->produk->foto) }}" alt=""></td>
<td class="details">
<a>{{ $item->produk->nama_produk }}</a>
<ul>
<li><span>Berat: {{ $item->produk->berat }} Gram</span></li>
</ul>
<ul>
<li><span>Stok: {{ $item->produk->stok }} Gram</span></li>
</ul>
</td>
<td class="price text-center"><strong>Rp. {{ number_format($item-
>harga, 0, ',', '.') }}</strong></td>
<td class="qty text-center">
<a> {{ $item->quantity }} </a>
</td>
<td class="total text-center"><strong class="primary-color">Rp. {{
number_format($item->harga * $item->quantity, 0, ',', '.') }}</strong></td>

</tr>
@endforeach
</tbody>
<tfoot>
<tr>
<th class="empty" colspan="3"></th>
<th>SUBTOTAL</th>
<th colspan="2" class="sub-total">Rp. {{ number_format($totalHarga, 0,
',', '.') }}</th>
</tr>
<tr>
<th class="empty" colspan="3"></th>
<th>Ongkos Kirim</th>
<td colspan="2">
Rp. {{ number_format($order->biaya_ongkir, 0, ',', '.') }} <br>
{{ $order->kurir.'.'.$order->layanan_ongkir.' *estimasi '. $order-
>estimasi_ongkir.' Hari'}}
@if(session('origin'))
<p>Kota asal: {{ $originName }} </p>
@endif

</td>
</tr>
<tr>
<th class="empty" colspan="3"></th>
<th>TOTAL BAYAR</th>
<th colspan="2" class="total">Rp. {{ number_format($totalHarga +
$order->biaya_ongkir, 0, ',', '.') }}</th>
</tr>
</tfoot>
</table>

<input type="hidden" name="total_price" value="{{ $totalHarga }}">


<input type="hidden" name="total_weight" value="{{ $totalBerat }}">
<div class="pull-right">
<button class="primary-btn" id="pay-button">Bayar Sekarang</button>
</div>
@else
<p>Keranjang belanja kosong.</p>
@endif
</div>
</div>
<script type="text/javascript">
var payButton = document.getElementById('pay-button');
payButton.addEventListener('click', function() {
window.snap.pay('{{ $snapToken }}', {

WEB PROGRAMMING III 100


onSuccess: function(result) {
alert("payment success!");
console.log(result);
window.location.href = "{{ route('order.complete') }}";
},
onPending: function(result) {
alert("waiting for your payment!");
console.log(result);
},
onError: function(result) {
alert("payment failed!");
console.log(result);
},
onClose: function() {
alert('you closed the popup without finishing the payment');
}
});
});
</script>

@endsection

10. Demikian juga kita update pada Controller.php yakni pada OrderController.php, dimana
kita akan update script function selectPayment, kemudian kita tambahkan function callback
& function complete. Untuk function complete terdapat 2 yakni:
public function complete() // Untuk kondisi local
untuk kondisi lokal atau ketika proyek belum memiliki domain. Fungsi ini digunakan saat
pengujian secara lokal.

// public function complete() // Untuk kondisi sudah memiliki domain


untuk kondisi ketika proyek sudah menggunakan domain.

Kemudian kita juga menambahkan function orderHistory dan invoiceFrontend untuk


menampilkan data setelah proses order selesai dilakukkan. Dan berikut script lengkap dari
OrderController.php:
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Models\Customer;
use App\Models\Produk;
use App\Models\Order;
use App\Models\OrderItem;
use Midtrans\Snap;
use Midtrans\Config;

class OrderController extends Controller


{
public function addToCart($id)
{
$customer = Customer::where('user_id', Auth::id())->first();
$produk = Produk::findOrFail($id);

$order = Order::firstOrCreate(
['customer_id' => $customer->id, 'status' => 'pending'],
['total_harga' => 0]
);

$orderItem = OrderItem::firstOrCreate(

WEB PROGRAMMING III 101


['order_id' => $order->id, 'produk_id' => $produk->id],
['quantity' => 1, 'harga' => $produk->harga]
);

if (!$orderItem->wasRecentlyCreated) {
$orderItem->quantity++;
$orderItem->save();
}

$order->total_harga += $produk->harga;
$order->save();

return redirect()->route('order.cart')->with('success', 'Produk berhasil


ditambahkan ke keranjang');
}

public function viewCart()


{
$customer = Customer::where('user_id', Auth::id())->first();
$order = Order::where('customer_id', $customer->id)->where('status', 'pending')-
>first();
// Pastikan $order ada
if (!$order) {
return redirect()->route('order.cart')->with('error', 'Order tidak
ditemukan.');
}
// Load relasi orderItems
$order->load('orderItems.produk');

return view('v_order.cart', compact('order'));


}

public function updateCart(Request $request, $id)


{
$customer = Customer::where('user_id', Auth::id())->first();;;
$order = Order::where('customer_id', $customer->id)->where('status', 'pending')-
>first();
if ($order) {
$orderItem = $order->orderItems()->where('id', $id)->first();
if ($orderItem) {
$quantity = $request->input('quantity');
if ($quantity > $orderItem->produk->stok) {
return redirect()->route('order.cart')->with('error', 'Jumlah produk
melebihi stok yang tersedia');
}
$order->total_harga -= $orderItem->harga * $orderItem->quantity;
$orderItem->quantity = $quantity;
$orderItem->save();
$order->total_harga += $orderItem->harga * $orderItem->quantity;
$order->save();
}
}
return redirect()->route('order.cart')->with('success', 'Jumlah produk berhasil
diperbarui');
}

public function removeFromCart(Request $request, $id)


{
$customer = Customer::where('user_id', Auth::id())->first();
$order = Order::where('customer_id', $customer->id)->where('status', 'pending')-
>first();

if ($order) {
$orderItem = OrderItem::where('order_id', $order->id)->where('produk_id', $id)-
>first();

WEB PROGRAMMING III 102


if ($orderItem) {
$order->total_harga -= $orderItem->harga * $orderItem->quantity;
$orderItem->delete();

if ($order->total_harga <= 0) {
$order->delete();
} else {
$order->save();
}
}
}
return redirect()->route('order.cart')->with('success', 'Produk berhasil dihapus
dari keranjang');
}

public function selectShipping(Request $request)


{
// Mendapatkan customer berdasarkan user yang login
$customer = Customer::where('user_id', Auth::id())->first();

// Pastikan order dengan status 'pending' ada untuk customer ini


$order = Order::where('customer_id', $customer->id)->where('status', 'pending')-
>first();

// Cek apakah order ada


if (!$order) {
return redirect()->route('order.cart')->with('error', 'Keranjang belanja
kosong.');
}

// Pastikan orderItems sudah dimuat menggunakan eager loading


$order->load('orderItems.produk');

// Lanjutkan ke view jika order ada


return view('v_order.select_shipping', compact('order'));
}

public function updateOngkir(Request $request)


{
$customer = Customer::where('user_id', Auth::id())->first();
$order = Order::where('customer_id', $customer->id)->where('status', 'pending')-
>first();

$origin = $request->input('city_origin'); // kode kota asal


$originName = $request->input('city_origin_name'); // nama kota asal

if ($order) {
// Simpan data ongkir ke dalam order
$order->kurir = $request->input('kurir');
$order->layanan_ongkir = $request->input('layanan_ongkir');
$order->biaya_ongkir = $request->input('biaya_ongkir');
$order->estimasi_ongkir = $request->input('estimasi_ongkir');
$order->total_berat = $request->input('total_berat');
$order->alamat = $request->input('alamat') . ', <br>' . $request-
>input('city_name') . ', <br>' . $request->input('province_name');
$order->pos = $request->input('pos');
$order->save();
// Simpan ke session flash agar bisa diakses di halaman tujuan
return redirect()->route('order.selectpayment')
->with('origin', $origin)
->with('originName', $originName);
}

return back()->with('error', 'Gagal menyimpan data ongkir');


}

WEB PROGRAMMING III 103


public function selectPayment()
{
$customer = Auth::user();
$order = Order::where('customer_id', $customer->customer->id)->where('status',
'pending')->first();

$origin = session('origin'); // Kode kota asal


$originName = session('originName'); // Nama kota asal

if (!$order) {
return redirect()->route('order.cart')->with('error', 'Keranjang belanja
kosong.');
}

// Muat relasi orderItems dan produk terkait


$order->load('orderItems.produk');

// Hitung total harga produk


$totalHarga = 0;
foreach ($order->orderItems as $item) {
$totalHarga += $item->harga * $item->quantity;
}

// Tambahkan biaya ongkir ke total harga


$grossAmount = $totalHarga + $order->biaya_ongkir;

// Midtrans configuration
Config::$serverKey = config('midtrans.server_key');
Config::$isProduction = false;
Config::$isSanitized = true;
Config::$is3ds = true;

// Generate unique order_id


$orderId = $order->id . '-' . time();

$params = [
'transaction_details' => [
'order_id' => $orderId,
'gross_amount' => (int) $grossAmount, // Pastikan gross_amount adalah
integer
],
'customer_details' => [
'first_name' => $customer->nama,
'email' => $customer->email,
'phone' => $customer->hp,
],
];

$snapToken = Snap::getSnapToken($params);
return view('v_order.select_payment', [
'order' => $order,
'origin' => $origin,
'originName' => $originName,
'snapToken' => $snapToken,
]);
}

public function callback(Request $request)


{
// dd($request->all());
$serverKey = config('midtrans.server_key');
$hashed = hash("sha512", $request->order_id . $request->status_code . $request-
>gross_amount . $serverKey);
if ($hashed == $request->signature_key) {

WEB PROGRAMMING III 104


$order = Order::find($request->order_id);
if ($order) {
$order->update(['status' => 'Paid']);
}
}
}

public function complete() // Untuk kondisi local


{
// Dapatkan customer yang login
$customer = Auth::user();

// Cari order dengan status 'pending' milik customer tersebut


$order = Order::where('customer_id', $customer->customer->id)
->where('status', 'pending')
->first();

if ($order) {
// Update status order menjadi 'Paid'
$order->status = 'Paid';
$order->save();
}

// Redirect ke halaman riwayat dengan pesan sukses


return redirect()->route('order.history')->with('success', 'Checkout berhasil');
}

// public function complete() // Untuk kondisi sudah memiliki domain


// {
// // Logika untuk halaman setelah pembayaran berhasil
// return redirect()->route('order.history')->with('success', 'Checkout berhasil');
// }

public function orderHistory()


{
$customer = Customer::where('user_id', Auth::id())->first();;;
// $orders = Order::where('customer_id', $customer->id)->where('status',
'completed')->get();
$statuses = ['Paid', 'Kirim', 'Selesai'];
$orders = Order::where('customer_id', $customer->id)
->whereIn('status', $statuses)
->orderBy('id', 'desc')
->get();
return view('v_order.history', compact('orders'));
}

public function invoiceFrontend($id)


{
$order = Order::findOrFail($id);
return view('backend.v_pesanan.invoice', [
'judul' => 'Pesanan',
'subJudul' => 'Pesanan Proses',
'judul' => 'Data Transaksi',
'order' => $order,
]);
}
}

WEB PROGRAMMING III 105


11. Pada web.php kita tambahkan route sesuai dengan function permintaan diatas, berikut
script lengkap pada Group route untuk customer sebagai berikut:
// Group route untuk customer
Route::middleware('is.customer')->group(function () {
// Route untuk menampilkan halaman akun customer
Route::get('/customer/akun/{id}', [CustomerController::class, 'akun'])
->name('customer.akun');

// Route untuk mengupdate data akun customer


Route::put('/customer/updateakun/{id}', [CustomerController::class, 'updateAkun'])
->name('customer.updateakun');

// Route untuk menambahkan produk ke keranjang


Route::post('add-to-cart/{id}', [OrderController::class, 'addToCart'])-
>name('order.addToCart');
Route::get('cart', [OrderController::class, 'viewCart'])->name('order.cart');
Route::post('cart/update/{id}', [OrderController::class, 'updateCart'])-
>name('order.updateCart');
Route::post('remove/{id}', [OrderController::class, 'removeFromCart'])-
>name('order.remove');
Route::post('select-shipping', [OrderController::class, 'selectShipping'])-
>name('order.select-shipping');
Route::post('update-ongkir', [OrderController::class, 'updateOngkir'])-
>name('order.update-ongkir');
//midtrans
Route::get('select-payment', [OrderController::class, 'selectPayment'])-
>name('order.selectpayment');
Route::post('/midtrans-callback', [OrderController::class, 'callback']);
Route::get('/order/complete', [OrderController::class, 'complete'])-
>name('order.complete');
// Route history
Route::get('history', [OrderController::class, 'orderHistory'])->name('order.history');
Route::get('order/invoice/{id}', [OrderController::class, 'invoiceFrontend'])-
>name('order.invoice');
});

12. Berikutnya pada view kita belum menambahakan view history.blade.php pada direktori
resources/views/v_order/history.blade.php sebagai berikut:
@extends('v_layouts.app')
@section('content')
<!-- template -->

@section('content')
<div class="col-md-12">
<div class="order-summary clearfix">
<div class="section-title">
<p>HISTORY</p>
<h3 class="title">HISTORY PESANAN</h3>
</div>
<!-- msgSuccess -->
@if(session()->has('success'))
<div class="alert alert-success alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-
label="Close"><span aria-hidden="true">&times;</span></button>
<strong>{{ session('success') }}</strong>
</div>
@endif
<!-- end msgSuccess -->
@if($orders->count() > 0)
<table class="shopping-cart-table table">
<thead>
<tr>
<th>ID Pesanan</th>
<th>Tanggal</th>

WEB PROGRAMMING III 106


<th>Total Bayar</th>
<th>Status</th>
<th>Detail</th>
</tr>
</thead>
<tbody>
@foreach($orders as $order)
<tr>
<td>{{ $order->id }}</td>
<td>{{ $order->created_at->format('d M Y H:i') }}</td>
<td>Rp. {{ number_format($order->total_harga + $order->biaya_ongkir, 0,
',', '.') }}
</td>
<td>
@if ($order->status == 'Paid')
Proses
@else
{{ $order->status }}
@endif
</td>
<td>
<button class="primary-btn" data-toggle="modal" data-
target="#orderDetailModal{{ $order->id }}">Lihat Detail</button>
<a href="{{ route('order.invoice', $order->id) }}" target="_blank">
<button type="button" class="primary-btn">Invoice</button>
</a>

<!-- Modal -->


<div class="modal fade" id="orderDetailModal{{ $order->id }}"
tabindex="-1" role="dialog" aria-labelledby="orderDetailModalLabel{{ $order->id }}" aria-
hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="orderDetailModalLabel{{
$order->id }}">Detail Pesanan #{{ $order->id }}</h5>
<button type="button" class="close" data-
dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<table class="table table-bordered">
<thead>
<tr>
<th>Nama Produk</th>
<th>Jumlah</th>
<th>Harga</th>
<th>Total</th>
</tr>
</thead>
<tbody>
@foreach($order->orderItems as $item)
<tr>
<td>{{ $item->produk->nama_produk
}}</td>
<td>{{ $item->quantity }}</td>
<td>Rp. {{ number_format($item->harga,
0, ',', '.') }}</td>
<td>Rp. {{ number_format($item->harga *
$item->quantity, 0, ',', '.') }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>

WEB PROGRAMMING III 107


<div class="modal-footer">
<button type="button" class="btn btn-secondary"
data-dismiss="modal">Tutup</button>
</div>
</div>
</div>
</div>
</td>
</tr>
@endforeach
</tbody>

</table>
@else
<p>Anda belum memiliki riwayat pesanan.</p>
@endif
</div>
</div>
@endsection

<div class="col-md-12">
<div class="order-summary clearfix">
<div class="section-title">
<h3 class="title">Order Review</h3>
</div>
@if($orders->count() > 0)
<table class="shopping-cart-table table">
<thead>
<tr>
<th>ID Pesanan</th>
<th>Tanggal</th>
<th>Total Harga</th>
<th>Status</th>
<th>Detail</th>
</tr>
</thead>
<tbody>
@foreach($order->orderItems as $item)
<tr>
<td>{{ $item->produk->nama_produk }}</td>
<td>{{ $item->quantity }}</td>
<td>Rp. {{ number_format($item->harga, 0, ',', '.') }}</td>
<td>Rp. {{ number_format($item->harga * $item->quantity, 0, ',', '.')
}}</td>
</tr>
@endforeach

</tbody>

</table>
<div class="pull-right">
<button class="primary-btn">Place Order</button>
</div>

@else
<p>Anda belum memiliki riwayat pesanan.</p>
@endif
</div>
</div>
<!-- end template-->
@endsection

WEB PROGRAMMING III 108


13. Demikian juga untuk view invoice, maka kita tambahkan pada file invoice.blade.php
pada direktori resources/views/v_order/invoice.blade.php sebagai berikut:
<style>
table {
border-collapse: collapse;
width: 100%;
border: 1px solid #ccc;
}

table tr td {
padding: 6px;
font-weight: normal;
border: 1px solid #ccc;
}

table th {
border: 1px solid #ccc;
}
</style>
<table>
<tr>
<td align="left">
<img src="{{ asset('image/logo.png') }}" width="8%">
</td>
</tr>
<tr>
<td align="left">
<h2>Detail Pesanan #{{ $order->id }}</h2>
<strong>Tanggal:</strong> {{ $order->created_at->format('d M Y H:i') }}
</td>
</tr>
</table>
<p></p>

<table>
<tr>
<td align="left" style="border: hidden;">
<h5>Pelanggan</h5>
<address>
Nama: {{ $order->customer->user->nama }}<br>
Email: {{ $order->customer->email }}<br>
Hp: {{ $order->customer->hp }}<br>
Alamat: <br>{!! $order->alamat !!} <br>
Kode Pos: {{ $order->pos }}
</address>
</td>
<td align="right" style="border: hidden;">
<h5>Ongkos Kirim</h5>
<address>
@if ($order->noresi)
No.Resi: {{ $order->noresi }} <br>
@else
No. Resi &lt;&lt;dalam proses&gt;&gt; <br>
@endif
Kurir: {{ $order->kurir }}<br>
Layanan: {{ $order->layanan_ongkir }}<br>
Estimasi: {{ $order->estimasi_ongkir }} Hari<br>
Berat: {{ $order->total_berat }} Gram<br>
</address>
</td>
</tr>
</table>
<p></p>

<table>

WEB PROGRAMMING III 109


<thead>
<tr>
<th>No</th>
<th>Produk</th>
<th class="text-center">Harga</th>
<th class="text-center">Quantity</th>
<th class="text-center">Total</th>

</tr>
</thead>
<tbody>
@php
$totalHarga = 0;
$totalBerat = 0;
@endphp
@foreach ($order->orderItems as $item)
@php
$totalHarga += $item->harga * $item->quantity;
$totalBerat += $item->produk->berat * $item->quantity;
@endphp
<tr>
<td> {{ $loop->iteration }}</td>
<td class="details">
<a>{{ $item->produk->nama_produk }} #{{ $item->produk->kategori-
>nama_kategori }}</a>
<ul>
<li><span>Berat: {{ $item->produk->berat }} Gram</span></li>
<li><span>Stok: {{ $item->produk->stok }} Gram</span></li>
</ul>
</td>
<td class="price text-center">Rp. {{ number_format($item->harga, 0, ',', '.')
}}</td>
<td class="qty text-center">
<a> {{ $item->quantity }} </a>
</td>
<td class="total text-center">Rp. {{ number_format($item->harga * $item-
>quantity, 0, ',', '.') }}</td>

</tr>
@endforeach
</tbody>
<tfoot>
<tr>
<th class="empty" colspan="3"></th>
<td>Subtotal</td>
<td colspan="2">Rp. {{ number_format($totalHarga, 0, ',', '.') }}</td>
</tr>
<tr>
<th class="empty" colspan="3"></th>
<td>Ongkos Kirim</td>
<td colspan="2">
Rp. {{ number_format($order->biaya_ongkir, 0, ',', '.') }}
</td>
</tr>
<tr>
<th class="empty" colspan="3"></th>
<td><b>Total Bayar</b></td>
<td colspan="2" class="total"> <b>Rp.
{{ number_format($totalHarga + $order->biaya_ongkir, 0, ',', '.')
}}</b> </td>
</tr>
</tfoot>
</table>

<script>
window.onload = function() {

WEB PROGRAMMING III 110


printStruk();
}

function printStruk() {
window.print();
}
</script>

14. Ketikan kita klik tombol Bayar Sekarang maka kita sudah berhasil menggunakan Snap
Token pada midtrans

Sebagai contoh kita akan melakukan pembayaran melalui Kredit/debit Card, dokumentasi
pada https://docs.midtrans.com/docs/snap-interactive-demo, sedangkan untuk pembayaran
lainnya yakni pada https://simulator.sandbox.midtrans.com/ sebagi berikut:

WEB PROGRAMMING III 111


Sebagai contoh kita gunakan Bank BNI

Pada https://simulator.sandbox.midtrans.com/ pilih Bank yang tadi kita pilih

4. Paste Virtual
account number dan
klik Inquire

3. Pilih bank BNI


pada Virtual
account

WEB PROGRAMMING III 112


5. akan menampilkan tagihan yang
akan dibayarkan & klik Play untuk
melakukan pembayaran

4. Pembayaran
Sukses dibayarkan

Kembali ke halaman website tunggu hingga status menjadi Payment Successfull, jika sudah
klik tombol OK untuk menuju kehalaman berikutnya

WEB PROGRAMMING III 113


Jika berhasil maka akan tampil ke halaman History pembelian, dan terpadat 2 aksi yakni
Lihat Detail dan Invice

Kita cek pada database maka transaksi ide=1 dengan status=Paid

Kali ini, kita dapat mengaktifkan aksi Keranjang dan History maka kita dapat menambahkan
script yakni pada app.blade.php

WEB PROGRAMMING III 114


<!-- Cart -->
<li class="header-cart dropdown default-dropdown">
<a href="{{ route('order.cart') }}">
<div class="header-btns-icon">
<i class="fa fa-shopping-cart"></i>
<!-- <span class="qty">3</span> -->
</div>
<strong class="text-uppercase">Keranjang</strong>

</a>
</li>
<!-- /Cart -->

Sedangkan untuk History sebagai berikut:


<!-- Account -->
@if (Auth::check())
<li class="header-account dropdown default-dropdown">
<div class="dropdown-toggle" role="button" data-toggle="dropdown" aria-expanded="true">
<div class="header-btns-icon">
<i class="fa fa-user-o"></i>
</div>
<strong class="text-uppercase">{{ Auth::user()->nama }}<i
class="fa fa-caret-down"></i></strong>
</div>
<ul class="custom-menu">
<li><a href="{{ route('customer.akun', ['id' => Auth::user()->id]) }}"><i class="fa
fa-user-o"></i> Akun Saya</a></li>
<li><a href="{{ route('order.history') }}"><i class="fa fa-check"></i>
History</a></li>
<li>
<a href="#"
onclick="event.preventDefault(); document.getElementById('keluar-
app').submit();"><i class="fa fa-power-off"></i> Keluar
</a>
<!-- form keluar app -->
<form id="keluar-app" action="{{ route('logout' ) }}" method="POST" class="d-
none">
@csrf
</form>
<!-- form keluar app end -->
</li>
</ul>
</li>
@else
<li class="header-account dropdown default-dropdown">
<div class="dropdown-toggle" role="button" data-toggle="dropdown" aria-expanded="true">
<div class="header-btns-icon">
<i class="fa fa-user-o"></i>
</div>
<strong class="text-uppercase">Akun Saya<i class="fa fa-caret-down"></i></strong>
</div>
<a href="{{ route('auth.redirect') }}" class="text-uppercase">Login</a>
</li>
@endif
<!-- /Account -->

WEB PROGRAMMING III 115


Minggu Ke-11
Manajemen Pemesanan

WEB PROGRAMMING III 116


Minggu Ke-13
Persentasi Tugas Kelompok

Presentasi Tugas Kelompok yakni mempresentasikan hasil dari project akhir yang
dikerjakan secara berkelompok dengan Indikator Penilaian:
1. Ketepatan waktu pengumpulan tugas
2. Kelengkapan makalah project
3. Dapat menjelaskan, menampilkan dan membuktikan hasil project akhir secara
berkelompok

WEB PROGRAMMING III 117


Minggu Ke-14
Persentasi Tugas Kelompok

Presentasi Tugas Kelompok yakni mempresentasikan hasil dari project akhir yang
dikerjakan secara berkelompok dengan Indikator Penilaian:
1. Ketepatan waktu pengumpulan tugas
2. Kelengkapan makalah project
3. Dapat menjelaskan, menampilkan dan membuktikan hasil project akhir secara
berkelompok

WEB PROGRAMMING III 118


Minggu Ke-15
Persentasi Tugas Kelompok

Presentasi Tugas Kelompok yakni mempresentasikan hasil dari project akhir yang
dikerjakan secara berkelompok dengan Indikator Penilaian:
1. Ketepatan waktu pengumpulan tugas
2. Kelengkapan makalah project
3. Dapat menjelaskan, menampilkan dan membuktikan hasil project akhir secara
berkelompok

WEB PROGRAMMING III 119

Anda mungkin juga menyukai